E-Commerce Integration
Complete Payment Flow (Flask)
Copy
import os
from flask import Flask, request, jsonify, redirect
from dotenv import load_dotenv
from inventpay import InventPayClient
load_dotenv()
app = Flask(__name__)
client = InventPayClient(api_key=os.getenv('INVENTPAY_API_KEY'))
# Store for orders (use a real database in production)
orders = {}
@app.route('/orders', methods=['POST'])
def create_order():
data = request.json
items = data['items']
customer_email = data['customer_email']
# Calculate total
total = sum(item['price'] * item['quantity'] for item in items)
try:
# Create payment
payment = client.payments.create(
amount=str(total),
currency='BTC',
description=f"Order for {customer_email}",
callback_url=f"{request.host_url}webhooks/inventpay",
metadata={
'order_type': 'ecommerce',
'customer_email': customer_email,
'items': items,
},
)
# Store order
orders[payment.id] = {
'id': payment.id,
'items': items,
'customer_email': customer_email,
'total': total,
'status': 'pending',
'payment_id': payment.id,
}
return jsonify({
'order_id': payment.id,
'payment_address': payment.payment_address,
'amount': payment.amount,
'currency': payment.currency,
'expires_at': payment.expires_at,
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/orders/<order_id>', methods=['GET'])
def get_order(order_id):
order = orders.get(order_id)
if not order:
return jsonify({'error': 'Order not found'}), 404
try:
# Get latest payment status
payment = client.payments.get(order['payment_id'])
return jsonify({
'order': order,
'payment': {
'status': payment.status,
'is_paid': payment.is_paid,
'is_confirmed': payment.is_confirmed,
'amount_received': payment.amount_received,
},
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/webhooks/inventpay', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-InventPay-Signature')
payload = request.get_data(as_text=True)
try:
event = client.webhooks.verify(payload, signature)
if event.type == 'payment.confirmed':
order = orders.get(event.data.id)
if order:
order['status'] = 'confirmed'
# Send confirmation email
send_order_confirmation(order)
# Fulfill order
fulfill_order(order)
return 'OK', 200
except Exception as e:
print(f"Webhook verification failed: {e}")
return 'Invalid signature', 400
def send_order_confirmation(order):
print(f"Sending confirmation email to {order['customer_email']}")
# Implement email sending
def fulfill_order(order):
print(f"Fulfilling order {order['id']}")
# Implement order fulfillment
if __name__ == '__main__':
app.run(debug=True)
SaaS Subscription (Django)
Monthly Subscription with Invoice
Copy
# models.py
from django.db import models
class Subscription(models.Model):
PLAN_CHOICES = [
('basic', 'Basic'),
('pro', 'Pro'),
('enterprise', 'Enterprise'),
]
STATUS_CHOICES = [
('active', 'Active'),
('inactive', 'Inactive'),
('cancelled', 'Cancelled'),
]
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
plan = models.CharField(max_length=20, choices=PLAN_CHOICES)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='inactive')
current_period_end = models.DateTimeField()
invoice_id = models.CharField(max_length=100, blank=True)
def __str__(self):
return f"{self.user.email} - {self.plan}"
# views.py
from django.shortcuts import redirect
from django.views.decorators.http import require_http_methods
from django.http import JsonResponse
from .models import Subscription
from .services import inventpay
from datetime import datetime, timedelta
@require_http_methods(["POST"])
def create_subscription(request):
user = request.user
plan = request.POST.get('plan')
prices = {
'basic': '9.99',
'pro': '29.99',
'enterprise': '99.99',
}
try:
invoice = inventpay.invoices.create(
amount=prices[plan],
currency='USD',
description=f'{plan.title()} Plan - Monthly',
callback_url=request.build_absolute_uri('/webhooks/inventpay/'),
metadata={
'type': 'subscription',
'user_id': str(user.id),
'plan': plan,
'billing_period': 'monthly',
},
)
# Create subscription record
subscription = Subscription.objects.create(
user=user,
plan=plan,
status='inactive',
current_period_end=datetime.now() + timedelta(days=30),
invoice_id=invoice.id,
)
return redirect(invoice.hosted_url)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
# webhooks.py
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from .services import inventpay
from .models import Subscription
@csrf_exempt
def handle_subscription_webhook(request):
signature = request.headers.get('X-InventPay-Signature')
payload = request.body.decode('utf-8')
try:
event = inventpay.webhooks.verify(payload, signature)
if event.type == 'invoice.paid':
metadata = event.data.metadata
user_id = metadata['user_id']
plan = metadata['plan']
# Activate subscription
subscription = Subscription.objects.get(
user_id=user_id,
invoice_id=event.data.id,
)
subscription.status = 'active'
subscription.current_period_end = datetime.now() + timedelta(days=30)
subscription.save()
# Grant plan access
grant_plan_access(user_id, plan)
return HttpResponse('OK', status=200)
except Exception as e:
return HttpResponse('Invalid signature', status=400)
def grant_plan_access(user_id, plan):
print(f"Granting {plan} access to user {user_id}")
# Implement access control logic
Marketplace Payout System
Seller Withdrawals
Copy
import os
from inventpay import InventPayClient
from decimal import Decimal
client = InventPayClient(api_key=os.getenv('INVENTPAY_API_KEY'))
class SellerWallet:
def __init__(self, seller_id, balance, withdrawal_address, currency):
self.seller_id = seller_id
self.balance = Decimal(str(balance))
self.withdrawal_address = withdrawal_address
self.currency = currency
self.pending_withdrawals = []
def request_withdrawal(self, amount: str):
amount_decimal = Decimal(amount)
if amount_decimal > self.balance:
raise ValueError("Insufficient balance")
try:
# Check platform balance
platform_balance = client.balances.get_by_currency(self.currency)
if Decimal(platform_balance.available) < amount_decimal:
raise ValueError("Insufficient platform balance")
# Create withdrawal
withdrawal = client.withdrawals.create(
amount=amount,
currency=self.currency,
address=self.withdrawal_address,
network='bitcoin', # or 'ethereum', etc.
)
# Deduct from seller balance
self.balance -= amount_decimal
self.pending_withdrawals.append(withdrawal.id)
return {
'withdrawal_id': withdrawal.id,
'status': withdrawal.status,
'fee': withdrawal.fee,
'estimated_arrival': withdrawal.estimated_arrival,
}
except Exception as e:
print(f"Withdrawal failed: {e}")
raise
def check_withdrawal_status(self, withdrawal_id):
withdrawal = client.withdrawals.get(withdrawal_id)
return {
'id': withdrawal.id,
'status': withdrawal.status,
'transaction_hash': withdrawal.transaction_hash,
'amount': withdrawal.amount,
'fee': withdrawal.fee,
}
# Example usage
seller_wallet = SellerWallet(
seller_id='seller_123',
balance=1000.0,
withdrawal_address='bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
currency='BTC',
)
try:
result = seller_wallet.request_withdrawal('0.5')
print(f"Withdrawal requested: {result['withdrawal_id']}")
# Check status
status = seller_wallet.check_withdrawal_status(result['withdrawal_id'])
print(f"Status: {status['status']}")
except Exception as e:
print(f"Error: {e}")
Gaming Platform (FastAPI)
In-Game Purchases with Async
Copy
from fastapi import FastAPI, HTTPException, Header, Request
from pydantic import BaseModel
from inventpay import AsyncInventPayClient
import os
app = FastAPI()
client = AsyncInventPayClient(api_key=os.getenv('INVENTPAY_API_KEY'))
class GamePurchase(BaseModel):
player_id: str
item_id: str
item_name: str
price: float
currency: str
class GameItem:
def __init__(self, item_id: str, name: str, price: float):
self.item_id = item_id
self.name = name
self.price = price
# Game items catalog
GAME_ITEMS = {
'sword_1': GameItem('sword_1', 'Legendary Sword', 99.99),
'armor_1': GameItem('armor_1', 'Dragon Armor', 149.99),
'potion_1': GameItem('potion_1', 'Health Potion Pack', 9.99),
}
@app.post('/game/purchase')
async def create_game_purchase(purchase: GamePurchase):
item = GAME_ITEMS.get(purchase.item_id)
if not item:
raise HTTPException(status_code=404, detail='Item not found')
try:
payment = await client.payments.create(
amount=str(item.price),
currency=purchase.currency,
description=f"{item.name} for player {purchase.player_id}",
callback_url=f"{os.getenv('APP_URL')}/webhooks/game",
metadata={
'type': 'game_purchase',
'player_id': purchase.player_id,
'item_id': item.item_id,
'item_name': item.name,
},
)
return {
'payment_id': payment.id,
'payment_address': payment.payment_address,
'amount': payment.amount,
'currency': payment.currency,
'qr_code': payment.qr_code_url,
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post('/webhooks/game')
async def handle_game_webhook(
request: Request,
x_inventpay_signature: str = Header(...),
):
body = await request.body()
payload = body.decode('utf-8')
try:
event = await client.webhooks.verify_async(payload, x_inventpay_signature)
if event.type == 'payment.confirmed':
metadata = event.data.metadata
player_id = metadata['player_id']
item_id = metadata['item_id']
item_name = metadata['item_name']
# Grant item to player
await grant_game_item(player_id, item_id)
# Log purchase
print(f"Player {player_id} purchased {item_name}")
# Send in-game notification
await send_in_game_notification(player_id, f"You received: {item_name}!")
return {'status': 'ok'}
except Exception:
raise HTTPException(status_code=400, detail='Invalid signature')
async def grant_game_item(player_id: str, item_id: str):
print(f"Granting item {item_id} to player {player_id}")
# Implement item granting logic via game server API
async def send_in_game_notification(player_id: str, message: str):
print(f"Sending notification to {player_id}: {message}")
# Implement real-time notification system
Balance Dashboard
Real-Time Balance Monitoring
Copy
import os
import time
from inventpay import InventPayClient
from decimal import Decimal
client = InventPayClient(api_key=os.getenv('INVENTPAY_API_KEY'))
def get_balance_summary():
"""Get summary of all balances"""
balances = client.balances.get_all()
summary = {
'total_usd': Decimal('0'),
'by_currency': {},
}
for balance in balances:
summary['by_currency'][balance.currency] = {
'available': balance.available,
'pending': balance.pending,
'total': balance.total,
}
# In production, convert to USD using exchange rates
# summary['total_usd'] += convert_to_usd(balance.total, balance.currency)
return summary
def monitor_balances(interval=60):
"""Monitor balance changes"""
previous_balances = {}
while True:
balances = client.balances.get_all()
for balance in balances:
previous = previous_balances.get(balance.currency)
if previous and previous['total'] != balance.total:
print(f"\n{balance.currency} balance changed:")
print(f" Previous: {previous['total']}")
print(f" Current: {balance.total}")
change = Decimal(balance.total) - Decimal(previous['total'])
print(f" Change: {change}")
previous_balances[balance.currency] = {
'available': balance.available,
'pending': balance.pending,
'total': balance.total,
}
time.sleep(interval)
def get_transaction_report(currency='BTC', days=30):
"""Generate transaction report"""
transactions = client.balances.get_transactions(currency, limit=1000)
report = {
'total_received': Decimal('0'),
'total_sent': Decimal('0'),
'transaction_count': len(transactions),
'transactions_by_type': {},
}
for tx in transactions:
amount = Decimal(tx.amount)
if tx.type == 'deposit':
report['total_received'] += amount
elif tx.type == 'withdrawal':
report['total_sent'] += amount
if tx.type not in report['transactions_by_type']:
report['transactions_by_type'][tx.type] = 0
report['transactions_by_type'][tx.type] += 1
return report
# Example usage
if __name__ == '__main__':
# Get balance summary
summary = get_balance_summary()
print("Balance Summary:", summary)
# Generate transaction report
report = get_transaction_report('BTC')
print("\nTransaction Report:", report)
# Start monitoring (uncomment to run)
# monitor_balances(60) # Check every minute
Testing Examples
Unit Tests with pytest
Copy
# test_inventpay.py
import pytest
import os
from inventpay import InventPayClient
from inventpay import InventPayError, AuthenticationError
@pytest.fixture
def client():
return InventPayClient(
api_key=os.getenv('INVENTPAY_TEST_API_KEY'),
base_url='https://sandbox-api.inventpay.io',
)
def test_create_payment(client):
payment = client.payments.create(
amount='100.00',
currency='BTC',
description='Test payment',
)
assert payment.id is not None
assert payment.amount == '100.00'
assert payment.currency == 'BTC'
assert payment.status == 'pending'
def test_get_payment(client):
# Create payment first
payment = client.payments.create(
amount='50.00',
currency='ETH',
)
# Retrieve it
retrieved = client.payments.get(payment.id)
assert retrieved.id == payment.id
assert retrieved.amount == payment.amount
def test_list_payments(client):
payments = client.payments.list(limit=10)
assert isinstance(payments, list)
assert len(payments) <= 10
def test_invalid_api_key():
client = InventPayClient(api_key='invalid_key')
with pytest.raises(AuthenticationError):
client.payments.list()
def test_get_balances(client):
balances = client.balances.get_all()
assert isinstance(balances, list)
for balance in balances:
assert balance.currency is not None
assert balance.available is not None
# Run tests with:
# pytest test_inventpay.py -v
Async Examples
Async E-Commerce with asyncio
Copy
import asyncio
import os
from inventpay import AsyncInventPayClient
async def process_multiple_orders(orders):
async with AsyncInventPayClient(api_key=os.getenv('INVENTPAY_API_KEY')) as client:
# Process all orders in parallel
tasks = [create_payment(client, order) for order in orders]
payments = await asyncio.gather(*tasks)
return payments
async def create_payment(client, order):
return await client.payments.create(
amount=str(order['total']),
currency='BTC',
description=f"Order #{order['id']}",
metadata={'order_id': order['id']},
)
# Example usage
orders = [
{'id': '001', 'total': 100.00},
{'id': '002', 'total': 250.00},
{'id': '003', 'total': 75.50},
]
payments = asyncio.run(process_multiple_orders(orders))
for payment in payments:
print(f"Created payment {payment.id}")
Error Handling Examples
Comprehensive Error Handler
Copy
from inventpay import (
InventPayClient,
InventPayError,
AuthenticationError,
RateLimitError,
ValidationError,
)
import time
client = InventPayClient(api_key=os.getenv('INVENTPAY_API_KEY'))
def create_payment_with_retry(amount, currency, max_retries=3):
"""Create payment with automatic retry on rate limit"""
for attempt in range(max_retries):
try:
return client.payments.create(amount=amount, currency=currency)
except AuthenticationError:
print("Authentication failed - check your API key")
raise
except RateLimitError as e:
if attempt < max_retries - 1:
wait_time = e.retry_after if hasattr(e, 'retry_after') else 60
print(f"Rate limited - waiting {wait_time}s before retry")
time.sleep(wait_time)
else:
raise
except ValidationError as e:
print(f"Validation failed: {e.errors}")
raise
except InventPayError as e:
print(f"API error: {e.message}")
raise
except Exception as e:
print(f"Unexpected error: {e}")
raise
# Usage
try:
payment = create_payment_with_retry('100.00', 'BTC')
print(f"Payment created: {payment.id}")
except Exception as e:
print(f"Failed to create payment: {e}")
