Advanced Features
This section covers advanced features and integration patterns with the Cardinity Python SDK, including webhooks, payment methods, advanced authentication, and complex payment scenarios.
Overview
Advanced features include:
Webhook Integration: Real-time payment notifications
Payment Methods: Alternative payment methods beyond cards
Advanced Authentication: OAuth and API key management
Batch Processing: Handle multiple payments efficiently
Payment Links: Generate payment links for customers
Complex Workflows: Multi-step payment processes
Analytics Integration: Payment data analysis and reporting
Webhook Integration
Set up webhooks for real-time payment notifications:
"""
Webhook Integration Example
This example shows how to set up and handle webhooks for payment notifications.
"""
import os
import hmac
import hashlib
import json
from flask import Flask, request, jsonify
from cardinity import Cardinity
app = Flask(__name__)
# Initialize Cardinity client
cardinity = Cardinity(
consumer_key=os.getenv("CARDINITY_CONSUMER_KEY", "your_consumer_key_here"),
consumer_secret=os.getenv("CARDINITY_CONSUMER_SECRET", "your_consumer_secret_here")
)
# Webhook configuration
WEBHOOK_SECRET = os.getenv("CARDINITY_WEBHOOK_SECRET", "your_webhook_secret")
def verify_webhook_signature(payload, signature):
"""Verify webhook signature for security."""
expected_signature = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
@app.route('/webhooks/cardinity', methods=['POST'])
def handle_cardinity_webhook():
"""Handle incoming Cardinity webhooks."""
try:
# Get raw payload and signature
payload = request.get_data()
signature = request.headers.get('X-Cardinity-Signature')
# Verify signature
if not verify_webhook_signature(payload, signature):
return jsonify({'error': 'Invalid signature'}), 401
# Parse webhook data
webhook_data = json.loads(payload)
# Process webhook based on event type
event_type = webhook_data.get('type')
if event_type == 'payment.approved':
handle_payment_approved(webhook_data)
elif event_type == 'payment.declined':
handle_payment_declined(webhook_data)
elif event_type == 'payment.pending':
handle_payment_pending(webhook_data)
elif event_type == 'refund.approved':
handle_refund_approved(webhook_data)
elif event_type == 'settlement.approved':
handle_settlement_approved(webhook_data)
else:
print(f"Unknown webhook event: {event_type}")
return jsonify({'status': 'processed'}), 200
except Exception as e:
print(f"Webhook processing error: {e}")
return jsonify({'error': 'Processing failed'}), 500
def handle_payment_approved(webhook_data):
"""Handle approved payment webhook."""
payment_id = webhook_data['data']['id']
amount = webhook_data['data']['amount']
print(f"✅ Payment approved: {payment_id} - {amount}")
# Update your database
# Send confirmation email
# Update order status
# Trigger fulfillment process
def handle_payment_declined(webhook_data):
"""Handle declined payment webhook."""
payment_id = webhook_data['data']['id']
decline_reason = webhook_data['data'].get('error', 'Unknown')
print(f"❌ Payment declined: {payment_id} - {decline_reason}")
# Update order status
# Send notification to customer
# Offer alternative payment methods
def handle_payment_pending(webhook_data):
"""Handle pending payment webhook (3DS in progress)."""
payment_id = webhook_data['data']['id']
print(f"⏳ Payment pending: {payment_id}")
# Update order status to pending
# Notify customer about 3DS requirement
def handle_refund_approved(webhook_data):
"""Handle approved refund webhook."""
refund_id = webhook_data['data']['id']
amount = webhook_data['data']['amount']
print(f"💰 Refund approved: {refund_id} - {amount}")
# Update order status
# Send refund confirmation
# Update inventory if needed
Payment Links
Generate payment links for customers:
def create_payment_link(order_details):
"""Create a payment link for customer checkout."""
try:
payment_link = cardinity.create_payment_link(
amount=order_details['amount'],
currency=order_details['currency'],
description=order_details['description'],
country=order_details['country'],
order_id=order_details['order_id'],
return_url=order_details['return_url'],
callback_url=order_details['callback_url'],
# Optional: Set expiration
expires_at=order_details.get('expires_at'),
# Optional: Custom styling
theme=order_details.get('theme', 'default')
)
print(f"✅ Payment link created: {payment_link['id']}")
print(f" Link URL: {payment_link['url']}")
print(f" Expires: {payment_link.get('expires_at', 'No expiration')}")
return payment_link
except Exception as e:
print(f"❌ Payment link creation failed: {e}")
return None
def payment_link_workflow():
"""Demonstrate payment link workflow."""
# Create order
order = {
'order_id': 'ORDER-2023-12345',
'amount': '99.99',
'currency': 'EUR',
'description': 'Premium Product Purchase',
'country': 'LT',
'return_url': 'https://your-site.com/payment/success',
'callback_url': 'https://your-site.com/webhooks/cardinity',
'expires_at': '2023-12-31T23:59:59Z',
'theme': 'modern'
}
# Create payment link
payment_link = create_payment_link(order)
if payment_link:
# Send link to customer (email, SMS, etc.)
send_payment_link_to_customer(
customer_email='customer@example.com',
payment_url=payment_link['url'],
order_details=order
)
return payment_link['url']
return None
def send_payment_link_to_customer(customer_email, payment_url, order_details):
"""Send payment link to customer via email."""
# Email implementation would go here
print(f"📧 Sending payment link to {customer_email}")
print(f" Payment URL: {payment_url}")
print(f" Order: {order_details['order_id']}")
Batch Payment Processing
Handle multiple payments efficiently:
import asyncio
import concurrent.futures
from typing import List, Dict
class BatchPaymentProcessor:
"""Process multiple payments in batches."""
def __init__(self, cardinity_client, max_workers=5):
self.cardinity = cardinity_client
self.max_workers = max_workers
def process_payment_batch(self, payment_requests: List[Dict]) -> List[Dict]:
"""Process multiple payments concurrently."""
print(f"🔄 Processing batch of {len(payment_requests)} payments...")
results = []
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
# Submit all payment requests
future_to_request = {
executor.submit(self._process_single_payment, request): request
for request in payment_requests
}
# Collect results as they complete
for future in concurrent.futures.as_completed(future_to_request):
request = future_to_request[future]
try:
result = future.result()
results.append({
'request': request,
'result': result,
'status': 'completed'
})
except Exception as e:
results.append({
'request': request,
'result': None,
'status': 'failed',
'error': str(e)
})
# Summary
successful = sum(1 for r in results if r['status'] == 'completed' and r['result'])
failed = len(results) - successful
print(f"✅ Batch processing completed: {successful} successful, {failed} failed")
return results
def _process_single_payment(self, payment_request):
"""Process a single payment request."""
try:
payment = self.cardinity.create_payment(**payment_request)
return payment
except Exception as e:
print(f"❌ Payment failed for {payment_request.get('order_id', 'unknown')}: {e}")
return None
def batch_processing_example():
"""Example of batch payment processing."""
# Create batch processor
processor = BatchPaymentProcessor(cardinity, max_workers=3)
# Define payment requests
payment_requests = [
{
'amount': '25.99',
'currency': 'EUR',
'description': 'Subscription renewal - Customer A',
'country': 'LT',
'order_id': 'SUB-001',
'payment_instrument': {
'pan': '4111111111111111',
'exp_month': 12,
'exp_year': 2025,
'cvc': '123',
'holder': 'Customer A'
}
},
{
'amount': '49.99',
'currency': 'EUR',
'description': 'Subscription renewal - Customer B',
'country': 'LT',
'order_id': 'SUB-002',
'payment_instrument': {
'pan': '5555555555554444',
'exp_month': 12,
'exp_year': 2025,
'cvc': '456',
'holder': 'Customer B'
}
},
{
'amount': '19.99',
'currency': 'EUR',
'description': 'Subscription renewal - Customer C',
'country': 'LT',
'order_id': 'SUB-003',
'payment_instrument': {
'pan': '378282246310005',
'exp_month': 12,
'exp_year': 2025,
'cvc': '1234',
'holder': 'Customer C'
}
}
]
# Process batch
results = processor.process_payment_batch(payment_requests)
return results
Advanced Authentication
Implement advanced authentication patterns:
class AdvancedCardinityAuth:
"""Advanced authentication patterns for Cardinity."""
def __init__(self):
self.tokens = {}
self.token_expiry = {}
def get_client_with_token_refresh(self, consumer_key, consumer_secret):
"""Get Cardinity client with automatic token refresh."""
# Check if we have a valid cached token
token_key = f"{consumer_key}:{consumer_secret}"
if token_key in self.tokens:
token = self.tokens[token_key]
# In a real implementation, check token expiry
# For now, assume token is valid
return Cardinity(
consumer_key=consumer_key,
consumer_secret=consumer_secret,
access_token=token
)
# Create new client (which will generate token)
client = Cardinity(
consumer_key=consumer_key,
consumer_secret=consumer_secret
)
# Cache the token (in real implementation, extract from client)
# self.tokens[token_key] = client.access_token
return client
def multi_account_payment_processing(self, account_configs, payment_request):
"""Process payment across multiple merchant accounts."""
for account_name, config in account_configs.items():
try:
print(f"🔄 Trying payment with account: {account_name}")
client = self.get_client_with_token_refresh(
config['consumer_key'],
config['consumer_secret']
)
payment = client.create_payment(**payment_request)
if payment and payment['status'] == 'approved':
print(f"✅ Payment successful with account: {account_name}")
return {
'success': True,
'payment': payment,
'account': account_name
}
except Exception as e:
print(f"❌ Payment failed with account {account_name}: {e}")
continue
return {
'success': False,
'error': 'All accounts failed'
}
Payment Analytics
Implement payment analytics and reporting:
from datetime import datetime, timedelta
import pandas as pd
from collections import defaultdict
class PaymentAnalytics:
"""Payment analytics and reporting."""
def __init__(self, cardinity_client):
self.cardinity = cardinity_client
self.payment_data = []
def collect_payment_data(self, start_date, end_date):
"""Collect payment data for analysis."""
# In production, this would fetch from your database
# For demo, we'll simulate payment data
sample_payments = [
{
'id': 'pay_001',
'amount': 25.99,
'currency': 'EUR',
'status': 'approved',
'created': '2023-12-01T10:00:00Z',
'country': 'LT',
'card_brand': 'visa',
'payment_method': 'card'
},
{
'id': 'pay_002',
'amount': 49.99,
'currency': 'EUR',
'status': 'declined',
'created': '2023-12-01T11:00:00Z',
'country': 'LV',
'card_brand': 'mastercard',
'payment_method': 'card'
},
# Add more sample data...
]
self.payment_data = sample_payments
return self.payment_data
def generate_payment_report(self, start_date, end_date):
"""Generate comprehensive payment report."""
data = self.collect_payment_data(start_date, end_date)
if not data:
return {'error': 'No payment data available'}
# Convert to DataFrame for analysis
df = pd.DataFrame(data)
df['created'] = pd.to_datetime(df['created'])
df['amount'] = pd.to_numeric(df['amount'])
# Calculate metrics
total_transactions = len(df)
total_amount = df['amount'].sum()
approved_transactions = len(df[df['status'] == 'approved'])
approval_rate = (approved_transactions / total_transactions) * 100
# Group by status
status_breakdown = df.groupby('status').agg({
'id': 'count',
'amount': 'sum'
}).to_dict()
# Group by country
country_breakdown = df.groupby('country').agg({
'id': 'count',
'amount': 'sum'
}).to_dict()
# Group by card brand
card_brand_breakdown = df.groupby('card_brand').agg({
'id': 'count',
'amount': 'sum'
}).to_dict()
# Daily trends
df['date'] = df['created'].dt.date
daily_trends = df.groupby('date').agg({
'id': 'count',
'amount': 'sum'
}).to_dict()
report = {
'period': {
'start': start_date,
'end': end_date
},
'summary': {
'total_transactions': total_transactions,
'total_amount': round(total_amount, 2),
'approved_transactions': approved_transactions,
'approval_rate': round(approval_rate, 2),
'average_transaction': round(total_amount / total_transactions, 2)
},
'breakdowns': {
'by_status': status_breakdown,
'by_country': country_breakdown,
'by_card_brand': card_brand_breakdown
},
'trends': {
'daily': daily_trends
}
}
return report
def fraud_detection_analysis(self):
"""Analyze payment patterns for fraud detection."""
# Implement fraud detection logic
suspicious_patterns = []
# Pattern 1: Multiple failed attempts from same IP
# Pattern 2: Unusual geographic patterns
# Pattern 3: High-value transactions outside normal hours
# Pattern 4: Multiple different cards from same customer
return {
'suspicious_patterns': suspicious_patterns,
'recommendations': [
'Review high-risk transactions manually',
'Implement additional verification for suspicious patterns',
'Monitor geographic transaction patterns'
]
}
Complex Payment Workflows
Implement complex multi-step payment workflows:
class PaymentWorkflowEngine:
"""Engine for complex payment workflows."""
def __init__(self, cardinity_client):
self.cardinity = cardinity_client
self.workflows = {}
def create_split_payment_workflow(self, total_amount, split_config):
"""Create workflow for split payments (marketplace scenario)."""
workflow_id = f"split_{int(time.time())}"
workflow = {
'id': workflow_id,
'type': 'split_payment',
'total_amount': float(total_amount),
'splits': split_config,
'status': 'pending',
'payments': [],
'created': datetime.utcnow().isoformat()
}
self.workflows[workflow_id] = workflow
return workflow_id
def execute_split_payment(self, workflow_id, payment_instrument):
"""Execute split payment workflow."""
workflow = self.workflows.get(workflow_id)
if not workflow:
return {'error': 'Workflow not found'}
results = []
# Process each split
for split in workflow['splits']:
try:
payment = self.cardinity.create_payment(
amount=str(split['amount']),
currency='EUR',
description=f"Split payment - {split['description']}",
country='LT',
order_id=f"{workflow_id}_{split['recipient']}",
payment_instrument=payment_instrument
)
results.append({
'recipient': split['recipient'],
'amount': split['amount'],
'payment_id': payment['id'],
'status': payment['status']
})
workflow['payments'].append(payment)
except Exception as e:
results.append({
'recipient': split['recipient'],
'amount': split['amount'],
'payment_id': None,
'status': 'failed',
'error': str(e)
})
# Update workflow status
successful_payments = sum(1 for r in results if r['status'] == 'approved')
if successful_payments == len(workflow['splits']):
workflow['status'] = 'completed'
elif successful_payments > 0:
workflow['status'] = 'partial'
else:
workflow['status'] = 'failed'
return {
'workflow_id': workflow_id,
'status': workflow['status'],
'results': results
}
def create_subscription_with_trial(self, trial_period_days, subscription_amount):
"""Create subscription workflow with trial period."""
workflow_id = f"trial_sub_{int(time.time())}"
workflow = {
'id': workflow_id,
'type': 'trial_subscription',
'trial_period_days': trial_period_days,
'subscription_amount': subscription_amount,
'status': 'trial_active',
'trial_start': datetime.utcnow().isoformat(),
'trial_end': (datetime.utcnow() + timedelta(days=trial_period_days)).isoformat(),
'payments': []
}
self.workflows[workflow_id] = workflow
return workflow_id
def convert_trial_to_subscription(self, workflow_id, payment_instrument):
"""Convert trial to paid subscription."""
workflow = self.workflows.get(workflow_id)
if not workflow or workflow['type'] != 'trial_subscription':
return {'error': 'Invalid trial workflow'}
try:
# Create initial subscription payment
payment = self.cardinity.create_payment(
amount=str(workflow['subscription_amount']),
currency='EUR',
description='Trial conversion - First subscription payment',
country='LT',
payment_instrument=payment_instrument
)
if payment['status'] == 'approved':
workflow['status'] = 'subscription_active'
workflow['subscription_payment_id'] = payment['id']
workflow['payments'].append(payment)
return {
'success': True,
'workflow_id': workflow_id,
'payment_id': payment['id'],
'status': 'subscription_active'
}
else:
workflow['status'] = 'conversion_failed'
return {
'success': False,
'workflow_id': workflow_id,
'error': 'Payment failed'
}
except Exception as e:
workflow['status'] = 'conversion_failed'
return {
'success': False,
'workflow_id': workflow_id,
'error': str(e)
}
Advanced Security Features
Implement advanced security patterns:
import hashlib
import secrets
from cryptography.fernet import Fernet
class PaymentSecurity:
"""Advanced security features for payment processing."""
def __init__(self):
self.encryption_key = Fernet.generate_key()
self.cipher = Fernet(self.encryption_key)
def tokenize_payment_data(self, payment_instrument):
"""Tokenize sensitive payment data."""
# Generate unique token
token = secrets.token_urlsafe(32)
# Encrypt payment data
encrypted_data = self.cipher.encrypt(
json.dumps(payment_instrument).encode()
)
# Store mapping (in production, use secure storage)
# self.token_store[token] = encrypted_data
return {
'token': token,
'encrypted_data': encrypted_data.decode(),
'last_four': payment_instrument.get('pan', '')[-4:] if payment_instrument.get('pan') else '',
'card_brand': self._detect_card_brand(payment_instrument.get('pan', ''))
}
def detokenize_payment_data(self, token, encrypted_data):
"""Decrypt tokenized payment data."""
try:
decrypted_data = self.cipher.decrypt(encrypted_data.encode())
payment_instrument = json.loads(decrypted_data.decode())
return payment_instrument
except Exception as e:
print(f"Detokenization failed: {e}")
return None
def _detect_card_brand(self, pan):
"""Detect card brand from PAN."""
if not pan or len(pan) < 4:
return 'unknown'
first_four = pan[:4]
if first_four.startswith('4'):
return 'visa'
elif first_four.startswith(('51', '52', '53', '54', '55')):
return 'mastercard'
elif first_four.startswith(('34', '37')):
return 'amex'
else:
return 'unknown'
def generate_payment_hash(self, payment_data):
"""Generate hash for payment integrity verification."""
# Create canonical string from payment data
canonical_string = '|'.join([
str(payment_data.get('amount', '')),
str(payment_data.get('currency', '')),
str(payment_data.get('description', '')),
str(payment_data.get('order_id', ''))
])
# Generate hash
payment_hash = hashlib.sha256(canonical_string.encode()).hexdigest()
return payment_hash
def verify_payment_integrity(self, payment_data, expected_hash):
"""Verify payment data integrity."""
calculated_hash = self.generate_payment_hash(payment_data)
return calculated_hash == expected_hash
Integration Testing
Comprehensive integration testing:
import unittest
from unittest.mock import Mock, patch
class AdvancedIntegrationTests(unittest.TestCase):
"""Advanced integration tests for payment workflows."""
def setUp(self):
self.cardinity = Cardinity(
consumer_key="test_key",
consumer_secret="test_secret"
)
def test_complete_payment_workflow(self):
"""Test complete payment workflow from creation to webhook."""
# Mock payment creation
with patch.object(self.cardinity, 'create_payment') as mock_create:
mock_create.return_value = {
'id': 'test_payment_123',
'status': 'approved',
'amount': '25.99',
'currency': 'EUR'
}
# Create payment
payment = self.cardinity.create_payment(
amount='25.99',
currency='EUR',
description='Test payment',
country='LT',
payment_instrument={
'pan': '4111111111111111',
'exp_month': 12,
'exp_year': 2025,
'cvc': '123',
'holder': 'Test User'
}
)
self.assertEqual(payment['status'], 'approved')
self.assertEqual(payment['amount'], '25.99')
def test_error_handling_workflow(self):
"""Test error handling in payment workflows."""
with patch.object(self.cardinity, 'create_payment') as mock_create:
mock_create.side_effect = ValidationError("Invalid amount")
with self.assertRaises(ValidationError):
self.cardinity.create_payment(
amount='invalid',
currency='EUR',
description='Test',
country='LT',
payment_instrument={
'pan': '4111111111111111',
'exp_month': 12,
'exp_year': 2025,
'cvc': '123',
'holder': 'Test User'
}
)
def test_webhook_processing(self):
"""Test webhook processing workflow."""
webhook_data = {
'type': 'payment.approved',
'data': {
'id': 'test_payment_123',
'status': 'approved',
'amount': '25.99'
}
}
# Test webhook handling
with patch('your_module.handle_payment_approved') as mock_handler:
handle_cardinity_webhook(webhook_data)
mock_handler.assert_called_once()
Performance Optimization
Optimize payment processing performance:
import asyncio
import aiohttp
from cachetools import TTLCache
class PerformanceOptimizedPayments:
"""Performance-optimized payment processing."""
def __init__(self):
self.cache = TTLCache(maxsize=1000, ttl=300) # 5-minute cache
self.session = aiohttp.ClientSession()
async def create_payment_async(self, payment_data):
"""Asynchronous payment creation."""
# Check cache first
cache_key = self._generate_cache_key(payment_data)
if cache_key in self.cache:
return self.cache[cache_key]
# Create payment asynchronously
async with self.session.post(
'https://api.cardinity.com/v1/payments',
json=payment_data,
headers=self._get_auth_headers()
) as response:
result = await response.json()
# Cache successful results
if result.get('status') == 'approved':
self.cache[cache_key] = result
return result
def _generate_cache_key(self, payment_data):
"""Generate cache key for payment data."""
# Use order_id as cache key if available
return payment_data.get('order_id', str(hash(str(payment_data))))
def _get_auth_headers(self):
"""Get authentication headers."""
# Implement OAuth or API key authentication
return {
'Authorization': 'Bearer your_token_here',
'Content-Type': 'application/json'
}
async def bulk_payment_processing(self, payment_requests):
"""Process multiple payments concurrently."""
tasks = []
for payment_request in payment_requests:
task = asyncio.create_task(
self.create_payment_async(payment_request)
)
tasks.append(task)
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
Production Deployment
Production deployment considerations:
"""
Production Deployment Best Practices
"""
def production_deployment_checklist():
"""Production deployment checklist for advanced features."""
checklist = {
'Security': [
'Use HTTPS for all communications',
'Implement proper API key management',
'Set up webhook signature verification',
'Enable rate limiting',
'Implement IP whitelisting where appropriate',
'Use environment variables for secrets'
],
'Monitoring': [
'Set up payment processing metrics',
'Implement error rate monitoring',
'Configure performance monitoring',
'Set up webhook delivery monitoring',
'Create alerting for failed payments'
],
'Scalability': [
'Implement connection pooling',
'Set up load balancing',
'Configure auto-scaling',
'Implement caching strategies',
'Optimize database queries'
],
'Reliability': [
'Implement circuit breaker pattern',
'Set up retry mechanisms',
'Configure graceful degradation',
'Implement health checks',
'Set up backup payment methods'
],
'Compliance': [
'Ensure PCI DSS compliance',
'Implement PSD2 requirements',
'Set up audit logging',
'Configure data retention policies',
'Implement GDPR compliance'
]
}
for category, items in checklist.items():
print(f"\n{category}:")
for item in items:
print(f" ☐ {item}")
Next Steps
After implementing advanced features:
Performance Monitoring: Set up comprehensive monitoring and alerting
Security Audit: Regular security audits and penetration testing
Compliance Review: Ensure ongoing compliance with regulations
Feature Enhancement: Continuously improve based on usage patterns
Documentation Updates: Keep documentation current with new features
Team Training: Train team members on advanced features and best practices