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:

  1. Webhook Integration: Real-time payment notifications

  2. Payment Methods: Alternative payment methods beyond cards

  3. Advanced Authentication: OAuth and API key management

  4. Batch Processing: Handle multiple payments efficiently

  5. Payment Links: Generate payment links for customers

  6. Complex Workflows: Multi-step payment processes

  7. 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

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:

  1. Performance Monitoring: Set up comprehensive monitoring and alerting

  2. Security Audit: Regular security audits and penetration testing

  3. Compliance Review: Ensure ongoing compliance with regulations

  4. Feature Enhancement: Continuously improve based on usage patterns

  5. Documentation Updates: Keep documentation current with new features

  6. Team Training: Train team members on advanced features and best practices