Troubleshooting Guide

Solutions to common issues when using dj-payfast.

Quick Diagnosis

Use this flowchart to quickly identify your issue:

Can't install dj-payfast?
└─> See: Installation Issues

Payment not creating?
└─> See: Payment Creation Issues

Can't reach PayFast?
└─> See: Payment Processing Issues

Webhook not firing?
└─> See: Webhook Issues

Signature errors?
└─> See: Security/Signature Issues

Database errors?
└─> See: Database Issues

Installation Issues

Issue: pip install fails

Symptoms:

ERROR: Could not find a version that satisfies the requirement dj-payfast

Solutions:

  1. Update pip:

    python -m pip install --upgrade pip
    
  2. Check Python version:

    python --version  # Must be 3.8+
    
  3. Use specific version:

    pip install dj-payfast==0.1.8
    
  4. Install from GitHub:

    pip install git+https://github.com/carrington-dev/dj-payfast.git
    

Issue: Import error after installation

Symptoms:

ImportError: No module named 'payfast'

Solutions:

  1. Verify installation:

    pip list | grep dj-payfast
    
  2. Check virtual environment:

    which python
    # Should be in your project's venv
    
  3. Add to INSTALLED_APPS:

    # settings.py
    INSTALLED_APPS = [
        # ...
        'payfast',  # Add this
    ]
    
  4. Run migrations:

    python manage.py migrate
    

Payment Creation Issues

Issue: Payment form not displaying

Symptoms: Blank page or missing form

Solutions:

  1. Check template exists:

    # Verify template path
    'payfast/checkout.html'
    
  2. Check context data:

    def checkout_view(request):
        # Ensure you're passing these
        return render(request, 'payfast/checkout.html', {
            'form': form,
            'payment': payment,
        })
    
  3. Debug template:

    {# In template #}
    {{ form.errors }}
    {{ payment }}
    

Issue: Missing required fields

Symptoms:

IntegrityError: NOT NULL constraint failed

Solution:

Ensure all required fields are provided:

# Required fields
payment = PayFastPayment.objects.create(
    m_payment_id=str(uuid.uuid4()),  # Required & unique
    amount=99.99,                     # Required
    item_name='Product Name',         # Required
    email_address='user@example.com', # Required
)

Issue: Duplicate payment_id error

Symptoms:

IntegrityError: UNIQUE constraint failed: payfast_payfastpayment.m_payment_id

Solutions:

  1. Generate unique IDs:

    import uuid
    
    m_payment_id = str(uuid.uuid4())
    # Or timestamp-based:
    from django.utils import timezone
    m_payment_id = f"{user.id}_{int(timezone.now().timestamp())}"
    
  2. Check for existing payment:

    if PayFastPayment.objects.filter(m_payment_id=payment_id).exists():
        # Reuse existing payment
        payment = PayFastPayment.objects.get(m_payment_id=payment_id)
    else:
        # Create new payment
        payment = PayFastPayment.objects.create(...)
    

Payment Processing Issues

Issue: Can’t reach PayFast

Symptoms: Connection timeout or error

Solutions:

  1. Check internet connection:

    ping sandbox.payfast.co.za
    
  2. Verify PAYFAST_TEST_MODE:

    # settings.py
    PAYFAST_TEST_MODE = True  # For sandbox
    # False for production
    
  3. Check firewall:

    Ensure outbound HTTPS (port 443) is allowed

Issue: Redirect not working

Symptoms: Form submits but nothing happens

Solutions:

  1. Check form action URL:

    <form action="{{ form.get_action_url }}" method="post">
    
  2. Verify merchant credentials:

    # settings.py
    PAYFAST_MERCHANT_ID = '10000100'  # Check this is correct
    PAYFAST_MERCHANT_KEY = '46f0cd694581a'
    
  3. Check JavaScript errors:

    Open browser console (F12) and look for errors

Issue: Payment stuck on “Processing”

Symptoms: Payment never completes or fails

Solutions:

  1. Check webhook configuration:

    notify_url = request.build_absolute_uri(
        reverse('payfast:notify')
    )
    
  2. Verify webhook URL is accessible:

    curl -X POST https://yourdomain.com/payfast/notify/
    # Should return 405 "Method Not Allowed"
    
  3. Check PayFast ITN logs:

    Login to PayFast dashboard → ITN History

Webhook Issues

Issue: Webhook never fires

Symptoms: Payment completes but status stays “pending”

Solutions:

  1. Verify webhook URL is public:

    # Test from external server
    curl -X POST https://yourdomain.com/payfast/notify/
    
  2. Check CSRF exemption:

    dj-payfast does this automatically, but verify:

    @csrf_exempt
    def webhook_view(request):
        pass
    
  3. Use ngrok for local testing:

    ngrok http 8000
    # Use ngrok URL as notify_url
    
  4. Check webhook logs:

    from payfast.models import PayFastNotification
    
    # Check for recent notifications
    notifications = PayFastNotification.objects.all().order_by('-created_at')[:10]
    
    for notif in notifications:
        print(f"Valid: {notif.is_valid}")
        print(f"Errors: {notif.validation_errors}")
    

Issue: Webhook returns 403 Forbidden

Symptoms: Webhook fails with 403 error

Solutions:

  1. Disable authentication requirement:

    # Don't require login for webhook
    # dj-payfast handles this automatically
    
  2. Check middleware:

    Ensure no middleware is blocking the request

  3. Verify ALLOWED_HOSTS:

    # settings.py (production)
    ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']
    

Issue: Webhook timeout

Symptoms: Webhook takes too long, PayFast retries

Solutions:

  1. Respond quickly:

    def webhook_view(request):
        # Process quickly
        payment_id = request.POST.get('m_payment_id')
    
        # Update database (fast)
        payment = PayFastPayment.objects.get(m_payment_id=payment_id)
        payment.mark_complete()
    
        # Queue slow tasks
        send_email.delay(payment.id)
    
        return HttpResponse('OK')  # Respond fast!
    
  2. Use Celery for slow tasks:

    pip install celery redis
    
  3. Optimize database queries:

    # Use select_related to reduce queries
    payment = PayFastPayment.objects.select_related('user').get(
        m_payment_id=payment_id
    )
    

Security/Signature Issues

Issue: Signature verification failed

Symptoms:

Invalid signature

Solutions:

  1. Check passphrase matches:

    # settings.py
    PAYFAST_PASSPHRASE = 'YourExactPassphrase'
    
    # Must match PayFast dashboard exactly
    
  2. Verify you’re using correct credentials:

    # Sandbox
    if PAYFAST_TEST_MODE:
        PAYFAST_PASSPHRASE = 'SandboxPassphrase'
    # Production
    else:
        PAYFAST_PASSPHRASE = 'ProductionPassphrase'
    
  3. Debug signature generation:

    from payfast.utils import generate_signature
    
    # Generate signature
    data = {
        'merchant_id': '10000100',
        'amount': '100.00',
        'item_name': 'Test',
    }
    
    signature = generate_signature(data, 'YourPassphrase')
    print(f"Generated: {signature}")
    
  4. Check for whitespace:

    # Remove any whitespace from passphrase
    PAYFAST_PASSPHRASE = 'YourPassphrase'.strip()
    

Issue: IP validation failing

Symptoms: Webhook rejected due to invalid IP

Solutions:

  1. Check if behind proxy:

    def get_client_ip(request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip
    
  2. Configure nginx for proxies:

    location /payfast/ {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://your-app;
    }
    
  3. Temporarily disable for testing:

    # Only for debugging!
    # utils.py
    def validate_ip(ip_address):
        return True  # REMOVE THIS IN PRODUCTION!
    

Database Issues

Issue: Migration errors

Symptoms:

django.db.utils.OperationalError: no such table

Solutions:

  1. Run migrations:

    python manage.py migrate payfast
    
  2. Check migrations exist:

    python manage.py showmigrations payfast
    
  3. Create missing migrations:

    python manage.py makemigrations payfast
    python manage.py migrate
    

Issue: Database locked

Symptoms (SQLite):

database is locked

Solutions:

  1. Use PostgreSQL in production:

    # settings.py
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'your_db',
        }
    }
    
  2. Increase SQLite timeout:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': 'db.sqlite3',
            'OPTIONS': {
                'timeout': 20,
            }
        }
    }
    

Configuration Issues

Issue: Settings not found

Symptoms:

AttributeError: 'Settings' object has no attribute 'PAYFAST_MERCHANT_ID'

Solution:

Add required settings:

# settings.py
PAYFAST_MERCHANT_ID = '10000100'
PAYFAST_MERCHANT_KEY = '46f0cd694581a'
PAYFAST_PASSPHRASE = 'your_passphrase'
PAYFAST_TEST_MODE = True

Issue: Environment variables not loading

Symptoms: Settings show None or empty values

Solutions:

  1. Install python-decouple:

    pip install python-decouple
    
  2. Create .env file:

    # .env
    PAYFAST_MERCHANT_ID=10000100
    PAYFAST_MERCHANT_KEY=46f0cd694581a
    PAYFAST_PASSPHRASE=your_passphrase
    
  3. Load in settings:

    # settings.py
    from decouple import config
    
    PAYFAST_MERCHANT_ID = config('PAYFAST_MERCHANT_ID')
    PAYFAST_MERCHANT_KEY = config('PAYFAST_MERCHANT_KEY')
    
  4. Add .env to .gitignore:

    # .gitignore
    .env
    *.env
    

Template Issues

Issue: Template not found

Symptoms:

TemplateDoesNotExist: payfast/checkout.html

Solutions:

  1. Check INSTALLED_APPS order:

    INSTALLED_APPS = [
        'payfast',  # Should be here
        # Your apps can override templates if listed after
    ]
    
  2. Verify APP_DIRS:

    TEMPLATES = [{
        'APP_DIRS': True,  # Must be True
    }]
    
  3. Create custom template:

    mkdir -p templates/payfast
    cp venv/lib/python3.x/site-packages/payfast/templates/payfast/checkout.html \
       templates/payfast/
    

Issue: Static files not loading

Symptoms: CSS/images missing

Solutions:

  1. Collect static files:

    python manage.py collectstatic
    
  2. Configure static settings:

    # settings.py
    STATIC_URL = '/static/'
    STATIC_ROOT = BASE_DIR / 'staticfiles'
    

Performance Issues

Issue: Slow payment creation

Solutions:

  1. Optimize database queries:

    # Use select_related for foreign keys
    payment = PayFastPayment.objects.select_related('user').get(id=1)
    
  2. Add database indexes:

    class PayFastPayment(models.Model):
        m_payment_id = models.CharField(max_length=100, db_index=True)
    
  3. Use connection pooling:

    pip install django-db-connection-pool
    

Issue: Webhook processing slow

Solutions:

  1. Use async tasks:

    from celery import shared_task
    
    @shared_task
    def process_webhook(payment_id):
        # Process in background
        pass
    
  2. Optimize validation:

    # Cache PayFast IP addresses
    from django.core.cache import cache
    
    def get_payfast_ips():
        ips = cache.get('payfast_ips')
        if not ips:
            ips = resolve_payfast_hosts()
            cache.set('payfast_ips', ips, 3600)
        return ips
    

Getting Help

If you can’t solve your issue:

  1. Check Existing Issues:

    https://github.com/carrington-dev/dj-payfast/issues

  2. Enable Debug Logging:

    # settings.py
    LOGGING = {
        'version': 1,
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
            },
        },
        'loggers': {
            'payfast': {
                'handlers': ['console'],
                'level': 'DEBUG',
            },
        },
    }
    
  3. Gather Information:

    • Django version: python manage.py --version

    • dj-payfast version: pip show dj-payfast

    • Python version: python --version

    • Error messages and stack traces

    • Relevant configuration (without credentials!)

  4. Create Issue:

    https://github.com/carrington-dev/dj-payfast/issues/new

Next Steps