Crypto Payments Guide - cmaliwal/aiagent-payments GitHub Wiki
USDT ERC-20 Payments Guide
A comprehensive guide to integrating USDT (Tether) ERC-20 token payments with AIAgent Payments using web3.py and Infura.
🎯 Overview
USDT ERC-20 payments offer secure, fast, and cost-effective cryptocurrency transactions on the Ethereum network. AIAgent Payments supports USDT payments on both Ethereum mainnet and Sepolia testnet with comprehensive blockchain verification and security features.
🚀 Quick Start
Basic Setup
from aiagent_payments.providers.crypto import CryptoProvider
# Initialize USDT provider
provider = CryptoProvider(
wallet_address="0xYourWalletAddress",
infura_project_id="your_infura_project_id",
network="mainnet" # or "sepolia" for testing
)
## Sender Address Requirement for USDT ERC-20
When processing USDT ERC-20 payments, you must provide the sender's Ethereum address in the `metadata` as `sender_address`. The payment will only be credited if the funds are sent from this address.
Example:
```python
transaction = provider.process_payment(
user_id="user123",
amount=50.0,
currency="USD",
metadata={
"sender_address": "0xabcdef1234567890abcdef1234567890abcdef12", # REQUIRED
"description": "AI Agent Consultation"
}
)
Process a USDT payment
transaction = provider.process_payment( user_id="user123", amount=50.0, # $50.00 USD currency="USD", metadata={"description": "AI Agent Consultation"} )
print(f"Payment ID: {transaction.id}") print(f"USDT Amount: {transaction.metadata.get('usdt_amount')} USDT") print(f"Wallet Address: {transaction.metadata.get('wallet_address')}")
### Environment Configuration
```bash
# Required environment variables
export INFURA_PROJECT_ID="your_infura_project_id"
export WALLET_ADDRESS="0xYourWalletAddress"
# Optional: Network configuration
export CRYPTO_NETWORK="mainnet" # or "sepolia"
export CRYPTO_CONFIRMATIONS="12" # mainnet default
export CRYPTO_MAX_GAS_PRICE="100" # gwei
💰 Supported Networks
Ethereum Mainnet
# Production USDT payments
provider = CryptoProvider(
wallet_address="0xYourWalletAddress",
infura_project_id="your_infura_project_id",
network="mainnet",
confirmations_required=12,
max_gas_price_gwei=100
)
# Process mainnet payment
transaction = provider.process_payment(
user_id="user123",
amount=100.0, # $100.00
currency="USD",
metadata={"description": "AI Agent Service"}
)
print(f"Payment ID: {transaction.id}")
print(f"USDT Amount: {transaction.metadata.get('usdt_amount')} USDT")
print(f"Network: {provider.get_network_info()['name']}")
Sepolia Testnet
# Testing USDT payments
provider = CryptoProvider(
wallet_address="0xYourTestWalletAddress",
infura_project_id="your_infura_project_id",
network="sepolia",
confirmations_required=6,
max_gas_price_gwei=50
)
# Process testnet payment
transaction = provider.process_payment(
user_id="test_user",
amount=10.0, # $10.00
currency="USD",
metadata={"description": "Test payment"}
)
🔗 Payment Methods
Direct USDT Payments
# Accept direct USDT payments
transaction = provider.process_payment(
user_id="user123",
amount=25.0,
currency="USD",
metadata={"description": "AI Agent Service"}
)
print(f"USDT Address: {transaction.metadata.get('wallet_address')}")
print(f"Required Amount: {transaction.metadata.get('usdt_amount')} USDT")
print(f"Payment ID: {transaction.id}")
Payment Verification
# Monitor payment confirmation
def monitor_payment(transaction_id):
max_attempts = 20
for attempt in range(max_attempts):
if provider.verify_payment(transaction_id):
print(f"✅ Payment {transaction_id} confirmed!")
return True
time.sleep(30) # Wait 30 seconds
print(f"⏳ Waiting for confirmation... (attempt {attempt + 1})")
print(f"❌ Payment {transaction_id} not confirmed within time limit")
return False
# Use the monitoring function
if monitor_payment(transaction.id):
# Grant access to AI agent features
grant_access(transaction.user_id)
🔄 Payment Verification
Check Payment Status
# Check if payment has been received
payment_status = provider.get_payment_status("payment_123")
if payment_status == "confirmed":
print("✅ Payment confirmed on blockchain")
# Get transaction details
details = provider.get_transaction_details("payment_123")
print(f"Transaction Hash: {details.get('transaction_hash')}")
print(f"Confirmations: {details.get('confirmations')}")
elif payment_status == "pending":
print("⏳ Payment pending confirmation")
else:
print("❌ Payment not received")
Monitor Transactions
# Monitor blockchain for payments
def monitor_payments():
while True:
try:
# Get recent transactions
transactions = provider.list_transactions(limit=10)
for tx in transactions:
if tx['status'] == 'pending':
# Check if payment is confirmed
if provider.verify_payment(tx['id']):
print(f"✅ Payment {tx['id']} confirmed!")
# Grant access to AI agent features
grant_access(tx['user_id'])
elif tx['status'] == 'expired':
print(f"❌ Payment {tx['id']} expired")
# Handle expired payment
time.sleep(60) # Check every minute
except Exception as e:
print(f"Error monitoring payments: {e}")
time.sleep(60)
🛡️ Security Features
Address Validation
from web3 import Web3
# Validate wallet address
def validate_address(address):
try:
checksum_address = Web3.to_checksum_address(address)
return checksum_address
except Exception as e:
raise ValueError(f"Invalid wallet address: {e}")
# Use in provider initialization
wallet_address = validate_address("0xYourWalletAddress")
provider = CryptoProvider(
wallet_address=wallet_address,
infura_project_id="your_infura_project_id"
)
Gas Price Protection
# Set maximum gas price to prevent overpayment
provider = CryptoProvider(
wallet_address="0xYourWalletAddress",
infura_project_id="your_infura_project_id",
max_gas_price_gwei=100 # Maximum 100 gwei
)
# Check current gas price
network_info = provider.get_network_info()
print(f"Current gas price: {network_info.get('gas_price_gwei')} gwei")
🔧 Advanced Configuration
Custom Network Settings
# Custom configuration for production
provider = CryptoProvider(
wallet_address="0xYourWalletAddress",
infura_project_id="your_infura_project_id",
network="mainnet",
confirmations_required=12, # Wait for 12 confirmations
max_gas_price_gwei=100 # Maximum gas price
)
# Get network information
network_info = provider.get_network_info()
print(f"Network: {network_info['name']}")
print(f"Chain ID: {network_info['chain_id']}")
print(f"Block Time: {network_info['block_time']} seconds")
Health Monitoring
# Check provider health
health_status = provider.check_health()
print(f"Provider healthy: {health_status.is_healthy}")
print(f"Network: {health_status.details.get('network')}")
print(f"Block height: {health_status.details.get('block_height')}")
# Get provider capabilities
capabilities = provider.get_capabilities()
print(f"Supports refunds: {capabilities.supports_refunds}")
print(f"Supports partial refunds: {capabilities.supports_partial_refunds}")
print(f"Network info: {capabilities.network_info}")
💸 Refund Process
Manual Refunds
# Only refund confirmed payments
if provider.verify_payment(transaction.id):
refund_info = provider.refund_payment(transaction.id, amount=50.0)
print("Refund Instructions:")
print(refund_info['instructions'])
print(f"Transaction Hash: {refund_info['transaction_hash']}")
print(f"Wallet Address: {refund_info['wallet_address']}")
print(f"Amount: {refund_info['amount']} USD")
print(f"USDT Amount: {refund_info['usdt_amount']} USDT")
# Manual steps required:
# 1. Use your wallet to send refund
# 2. Track the refund transaction
# 3. Update your records
else:
print("Cannot refund - payment not confirmed")
🚨 Error Handling
Common Error Scenarios
try:
transaction = provider.process_payment(user_id, amount, "USD")
except ProviderError as e:
if "Invalid wallet address" in str(e):
print("❌ Check your wallet address format")
elif "Infura connection failed" in str(e):
print("❌ Check your Infura project ID and network connectivity")
elif "Gas price too high" in str(e):
print("❌ Gas price exceeds maximum limit")
else:
print(f"❌ Provider error: {e}")
except PaymentFailed as e:
print(f"❌ Payment failed: {e}")
except ValidationError as e:
print(f"❌ Validation error: {e}")
Debug Mode
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
# This will show detailed web3 calls and responses
provider = CryptoProvider(
wallet_address="0xYourWalletAddress",
infura_project_id="your_infura_project_id",
network="sepolia" # Use testnet for debugging
)
📊 Transaction Management
List Transactions
# Get all transactions
all_transactions = provider.list_transactions()
# Get transactions for specific user
user_transactions = provider.list_transactions(user_id="user123")
# Get transactions by status
pending_transactions = provider.list_transactions(status="pending")
confirmed_transactions = provider.list_transactions(status="confirmed")
# Get recent transactions with limit
recent_transactions = provider.list_transactions(limit=10)
Transaction Details
# Get detailed transaction information
transaction_details = provider.get_transaction_details("payment_123")
print(f"Transaction ID: {transaction_details['id']}")
print(f"User ID: {transaction_details['user_id']}")
print(f"Amount: {transaction_details['amount']} USD")
print(f"USDT Amount: {transaction_details['usdt_amount']} USDT")
print(f"Status: {transaction_details['status']}")
print(f"Created: {transaction_details['created_at']}")
print(f"Transaction Hash: {transaction_details.get('transaction_hash')}")
print(f"Confirmations: {transaction_details.get('confirmations')}")
🔄 Migration Guide
From Old Crypto Provider
If migrating from the old BlockCypher/CoinGecko implementation:
-
Update Dependencies:
pip uninstall blockcypher coingecko pip install web3
-
Update API Keys:
# Remove old keys unset BLOCKCYPHER_TOKEN unset COINGECKO_API_KEY # Add new keys export INFURA_PROJECT_ID="your_infura_project_id" export WALLET_ADDRESS="0xYourWalletAddress"
-
Update Code:
# Old implementation provider = CryptoProvider(crypto_type="bitcoin") # New implementation provider = CryptoProvider( wallet_address="0xYourWalletAddress", infura_project_id="your_infura_project_id", network="mainnet" )
-
Test Thoroughly: Verify all payment flows work correctly
From Other Crypto Providers
If migrating from other crypto providers:
- Set up Infura: Create account and get project ID
- Configure Wallet: Ensure wallet address is valid and has ETH for gas
- Update Code: Use new provider interface
- Test on Sepolia: Test thoroughly on testnet before mainnet
- Monitor Transactions: Set up proper monitoring and alerts
🛠️ Troubleshooting
Common Issues
1. "Invalid wallet address" error
- Solution: Ensure wallet address is valid Ethereum address with checksum
- Use
Web3.to_checksum_address()
to validate
2. "Infura connection failed" error
- Solution: Check your Infura project ID and network connectivity
- Verify project is active and has sufficient credits
3. "Payment not confirmed" error
- Solution: Wait for blockchain confirmations (typically 1-6 blocks)
- Check network congestion and gas fees
4. "Gas price too high" error
- Solution: Adjust
max_gas_price_gwei
parameter or wait for lower gas prices - Monitor Ethereum network conditions
Network Status
# Check network status
network_info = provider.get_network_info()
print(f"Network: {network_info['name']}")
print(f"Chain ID: {network_info['chain_id']}")
print(f"Block Height: {network_info['block_height']}")
print(f"Gas Price: {network_info['gas_price_gwei']} gwei")
print(f"Block Time: {network_info['block_time']} seconds")
📈 Production Considerations
Wallet Management
- Use hardware wallets for large amounts
- Implement proper key management
- Set up secure wallet infrastructure
- Monitor wallet balances regularly
Network Monitoring
- Monitor gas fees and network congestion
- Set up alerts for failed transactions
- Track confirmation times
- Monitor Infura rate limits
Security
- Never expose private keys
- Use environment variables for sensitive data
- Implement proper access controls
- Regular security audits
🆘 Support
For issues with the USDT crypto provider:
- Check this guide for common solutions
- Review the error messages and logs
- Verify your Infura project ID and wallet address
- Test with the provided examples
- Check network status and gas prices
Remember: The USDT crypto provider is designed for production use with comprehensive security features. For testing, always use the Sepolia testnet first.