Table of contents

Troubleshooting

Precium S2S Troubleshooting Guide

This guide helps you diagnose and resolve common issues when integrating with the Precium S2S API.

Diagnostic Decision Tree

Use this flowchart to identify your issue category:

flowchart TD
    A[Issue Encountered] --> B{Error in Response?}
    B -->|Yes| C{HTTP Status?}
    B -->|No| D{Unexpected Behavior?}
    C -->|4xx| E[Client Error]
    C -->|5xx| F[Server Error]
    E --> G{Status Code}
    G -->|400| H[Bad Request - Check payload]
    G -->|401| I[Auth Error - Check API key]
    G -->|403| J[Forbidden - Check permissions]
    G -->|404| K[Not Found - Check endpoint/ID]
    G -->|422| L[Validation - Check data format]
    F --> M{Is it intermittent?}
    M -->|Yes| N[Retry with backoff]
    M -->|No| O[Contact support]
    D -->|3DS not completing| P[3DS Issues]
    D -->|Webhooks not arriving| Q[Webhook Issues]
    D -->|Charges failing| R[Payment Issues]

Authentication Issues

Issue: "401 Unauthorized" Response

Symptoms:

JSON

{
  "error": "Invalid or missing API key"
}

Solutions:

CauseSolution
Missing Authorization headerAdd Authorization: Bearer <api_key> to request
Using wrong key typeUse Standard key for most endpoints, S2S key for direct post
API key has whitespaceTrim whitespace from key
API key expired or revokedGenerate new key in dashboard
Using test key in productionSwitch to live keys

Verification:

BASH

curl -X GET https://gate.reviopay.com/api/v1/clients/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Issue: "403 Forbidden" Response

Common Causes:

  • S2S not enabled for your account
  • Attempting operations outside your permission scope
  • IP restrictions in place

Solution: Contact Precium support to verify your account permissions.

Request Validation Issues

Issue: "400 Bad Request" or "422 Unprocessable Entity"

Check these common mistakes:

Missing Required Fields

JSON

// Wrong
{
  "currency": "ZAR",
  "products": [{"name": "Test", "price": 100}]
}

// Correct
{
  "client_id": "uuid-here",
  "brand_id": "your-brand-id",
  "currency": "ZAR",
  "products": [{"name": "Test", "price": 100}],
  "success_redirect": "https://example.com/success",
  "failure_redirect": "https://example.com/failure"
}

Wrong Amount Format

JAVASCRIPT

// Wrong - decimal amount
{ "price": 299.00 }

// Correct - cents as integer
{ "price": 29900 }

Invalid Expiry Format

Wrong: 12/2028, 2028-12, 1228
Correct: 12/28 (MM/YY format)

Invalid Currency

Wrong: "Rand", "rand", "R"
Correct: "ZAR" (ISO 4217)

3DS Issues

Issue: 3DS Challenge Not Loading

flowchart TD
    A[3DS Not Loading] --> B{Form submitted correctly?}
    B -->|No| C[Fix form submission]
    B -->|Yes| D{Correct fields included?}
    D -->|No| E[Add MD, PaReq, TermUrl]
    D -->|Yes| F{Correct target URL?}
    F -->|No| G[Use 3ds.3ds_url]
    F -->|Yes| H[Check browser console for errors]

Required 3DS Form Fields:

HTML

<form method='POST' action='{{3ds_url}}'>
  <input type='hidden' name='MD' value='{{MD}}' />
  <input type='hidden' name='PaReq' value='{{PaReq}}' />
  <input type='hidden' name='TermUrl' value='{{callback_url}}' />
</form>

Issue: 3DS Callback Not Received

Causes:

  • callback_url not accessible from internet
  • Firewall blocking incoming requests
  • URL mismatch (http vs https)

Solution: Ensure your callback URL is publicly accessible, uses HTTPS in production, and returns HTTP 200 within 5 seconds.

Issue: 3ds_authentication_failed Error

Allow the customer to retry. If persistent with a specific card/bank, investigate with Precium support.

Direct Post Issues

Issue: "Direct Post URL Expired"

Cause: Direct post URLs are single-use and expire after the first submission.

Solution: Create a new purchase to get a fresh direct_post_url.

Issue: Card Data Submission Fails

CheckCorrect Format
Card numberDigits only, no spaces (e.g., 4000000000001091)
ExpiryMM/YY format (e.g., 12/28)
CVC3 digits (4 for Amex)
Content-Typeapplication/x-www-form-urlencoded
API KeyMust use S2S key, not Standard key

Working Example:

BASH

curl -X POST "DIRECT_POST_URL" \
  -H "Authorization: Bearer YOUR_S2S_API_KEY" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "cardholder_name=John Smith" \
  -d "card_number=4000000000001091" \
  -d "expires=12/28" \
  -d "cvc=123" \
  -d "remote_ip=196.21.45.123" \
  -d "user_agent=Mozilla/5.0" \
  -d "accept_header=text/html" \
  -d "language=en" \
  -d "java_enabled=false" \
  -d "javascript_enabled=true" \
  -d "color_depth=24" \
  -d "utc_offset=-120" \
  -d "screen_width=1920" \
  -d "screen_height=1080"

Charge/Payment Issues

Issue: Charge Returns Error

flowchart TD
    A[Charge Failed] --> B{Check error code}
    B --> C{Type?}
    C -->|Validation| D[Fix input data]
    C -->|Auth Failure| E[3DS not completed]
    C -->|Decline| F{Soft or Hard?}
    C -->|Network| G[Retry later]
    F -->|Soft| H[Can retry with same card]
    F -->|Hard| I[Request different card]
    E --> J[Complete 3DS first]
    D --> K[Correct and retry]

Pre-Authorization Issues

Issue: Capture Fails After Authorization

ErrorCauseSolution
authorization_expiredAuth held too longRe-authorize with customer
capture_amount_exceeds_authorizationCapturing more than authedReduce capture amount
already_capturedDuplicate captureCheck transaction status
already_voidedAuth was cancelledRe-authorize

Diagnostic Steps:

  1. Check purchase status — must be "authorized"
  2. Verify capture amount ≤ authorized amount
  3. Check authorization age (7-30 days typical expiry)

PYTHON

purchase = client.get_purchase(purchase_id)
if purchase['status'] != 'authorized':
    print(f"Cannot capture - status is: {purchase['status']}")

Issue: Partial Capture Not Working

Error:partial_capture_not_supported

Solution:

  • Capture the full amount and refund the difference, OR
  • Void and create a new authorization for the correct amount
  • Contact Precium to enable partial capture

Issue: Pre-Authorization Void Fails

PYTHON

purchase = client.get_purchase(purchase_id)
if purchase['status'] == 'paid':
    # Already captured - use refund
    client.refund_purchase(purchase_id)
elif purchase['status'] == 'authorized':
    # Can void
    client.void_purchase(purchase_id)

Zero Authorization Issues

Issue: Zero Auth Not Permitted

Error:zero_auth_not_permitted

Solutions:

  1. Contact Precium to enable zero authorization
  2. Use a minimum amount (e.g., R1.00) and immediately refund
  3. Use network token validation instead

Issue: Zero Auth Doesn't Generate Token

PYTHON

purchase = client.create_zero_auth_purchase(
    client_id=customer_id,
    currency='ZAR',
    success_redirect='https://...',
    failure_redirect='https://...',
    force_recurring=True  # Required for tokenization
)

External 3DS (MPI) Issues

Issue: External 3DS Data Rejected

Error:invalid_external_3ds_data

FieldRequiredFormat
is_external_3DSYestrue
authentication_transaction_idYesYour MPI's transaction ID
cavvYesBase64 encoded
eci_rawYes"05", "02", "06", "01", etc.
xid3DS1 onlyBase64 encoded
ds_trans_id3DS2 onlyUUID format

PYTHON

# Wrong - missing is_external_3DS
payment_details = {
    "card": {
        "cavv": "AAA...",
        "eci_raw": "05"
    }
}

# Correct
payment_details = {
    "card": {
        "is_external_3DS": True,  # Required
        "authentication_transaction_id": "YOUR_MPI_TX_ID",
        "cavv": "AAA...",
        "eci_raw": "05",
        "ds_trans_id": "uuid-here"  # For 3DS2
    }
}

Issue: External 3DS Brand Not Configured

Error:external_3ds_not_enabled

Cause: Must use a Non-3DS brand for external MPI. Contact Precium to create a Non-3DS brand.

Network Token Issues

Issue: Invalid Network Token

Error:invalid_network_token

Request fresh token from MDES/VTS and verify token requestor ID is correct.

Issue: Cryptogram Expired

Error:cryptogram_expired

PYTHON

# Request fresh cryptogram immediately before transaction
cryptogram = request_new_cryptogram_from_token_service()
# Submit transaction immediately after

Webhook Issues

Issue: Webhooks Not Arriving

flowchart TD
    A[Webhooks Not Arriving] --> B{URL configured in dashboard?}
    B -->|No| C[Configure webhook URL]
    B -->|Yes| D{Endpoint accessible?}
    D -->|No| E[Check firewall/DNS]
    D -->|Yes| F{SSL valid?}
    F -->|No| G[Fix SSL certificate]
    F -->|Yes| H{Returning 200?}
    H -->|No| I[Fix endpoint code]
    H -->|Yes| J[Check Precium webhook logs]

Verification:

BASH

curl -X POST https://your-webhook-url.com/precium \
  -H "Content-Type: application/json" \
  -d '{"test": true}'

Issue: Webhook Signature Verification Fails

CauseSolution
Wrong secretCopy fresh secret from dashboard
Parsed JSON instead of rawUse raw request body for signature
Encoding issuesEnsure UTF-8 encoding throughout
Missing timestampInclude timestamp in signature calculation

Debugging:

PYTHON

print(f"Received signature: {signature}")
print(f"Timestamp: {timestamp}")
print(f"Raw payload length: {len(payload)}")

message = f"{timestamp}.{payload.decode('utf-8')}"
computed = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
print(f"Computed signature: {computed}")
print(f"Match: {hmac.compare_digest(signature, computed)}")

Issue: Webhook Timeout Errors

Solution — Async Processing:

PYTHON

# Wrong - synchronous processing
@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.json
    process_payment(data)  # Slow - might timeout
    return '', 200

# Correct - acknowledge immediately, process async
@app.route('/webhook', methods=['POST'])
def webhook():
    if not verify_signature(request):
        return '', 401
    webhook_id = request.headers.get('X-Webhook-ID')
    queue.enqueue(process_webhook, request.json, webhook_id)
    return '', 200  # Return immediately

Issue: Duplicate Webhooks

PYTHON

import redis
redis_client = redis.Redis()

def handle_webhook(data, webhook_id):
    cache_key = f"webhook:{webhook_id}"
    if redis_client.exists(cache_key):
        return  # Already processed
    redis_client.setex(cache_key, 86400, "processing")
    try:
        process_event(data)
        redis_client.setex(cache_key, 86400, "completed")
    except Exception as e:
        redis_client.delete(cache_key)
        raise

Issue: Testing Webhooks Locally

BASH

# Install and start ngrok
ngrok http 3000
# Use the generated URL in Precium dashboard
# e.g., https://abc123.ngrok.io/webhooks/precium

Trigger a test webhook:

BASH

curl -X POST https://gate.reviopay.com/api/v1/webhooks/test/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "purchase.paid",
    "webhook_url": "https://abc123.ngrok.io/webhooks/precium"
  }'

Payment Decline Issues

Issue: "insufficient_funds"

  • Notify the customer of insufficient funds
  • Suggest retry after funding the account
  • Offer alternative payment methods
  • For MIT: Schedule retry per retry logic

Customer Message:"Your payment couldn't be processed due to insufficient funds. Please add funds to your account and try again."

Issue: "do_not_honour"

Potential Causes:

  • Card restrictions
  • Unusual spending pattern
  • Bank's fraud detection
  • Account issues

Actions:

  1. Suggest the customer contact their bank
  2. Offer an alternative payment method
  3. For MIT: Retry per schedule

Issue: Token-Based Charge Fails

Common Causes:

  • Token expired
  • Original card expired/replaced
  • Wrong previous_network_transaction_id
  • Incorrect original_amount_cents

Verification Checklist:

  • Token is from the same client
  • previous_network_transaction_id matches original CIT
  • original_amount_cents matches tokenization amount
  • is_recurring: true in purchase

Issue: Refund Amount Exceeds Original

PYTHON

original_amount = 29900
already_refunded = 5000
max_refundable = original_amount - already_refunded

if requested_refund > max_refundable:
    raise ValueError(f"Can only refund up to {max_refundable}")

Issue: Refund Pending for Days

ScenarioTimeline
Same-day (pre-settlement)Void — immediate
Post-settlement5-10 business days
International cardsUp to 15 business days

Testing Issues

Issue: Test Cards Not Working in Production

Test card numbers only work in the sandbox environment. Use real cards in production.

Issue: Real Cards Not Working in Sandbox

Sandbox doesn't process real cards. Use designated test card numbers for sandbox testing.

Performance Issues

Issue: API Requests Are Slow

Optimisation Checklist:

  1. Use connection pooling
  2. Implement request timeouts
  3. Cache client IDs (don't create new clients each time)
  4. Use webhooks instead of polling for status

PYTHON

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retry = Retry(total=3, backoff_factor=0.5)
adapter = HTTPAdapter(max_retries=retry, pool_connections=10, pool_maxsize=10)
session.mount('https://', adapter)

When to Contact Support

Contact Precium support (support@precium.com) when:

  • URGENT priority errors persist
  • Unexpected error codes not in documentation
  • Settlement or reconciliation discrepancies
  • Account configuration issues
  • Suspected fraud or security concerns

Include in Support Request:

  • Purchase ID(s)
  • Timestamp of issue
  • Full error response
  • Steps to reproduce
  • Expected vs actual behaviour

Error Code Quick Lookup

ErrorAction
insufficient_fundsRetry later, different card
do_not_honourRetry later, contact bank
expired_cardNew card required
3ds_authentication_failedRetry authentication
acquirer_configuration_errorContact support
invalid_card_numberCheck card number format
antifraud_generalDo not retry, investigate
authorization_expiredRe-authorize
capture_amount_exceeds_authorizationReduce capture amount
void_not_permittedCheck transaction status, use refund if settled
partial_capture_not_supportedCapture full amount or void and re-auth
refund_amount_exceeds_originalCheck total refunded amount
invalid_external_3ds_dataVerify all required 3DS fields
zero_auth_not_permittedContact support to enable
invalid_network_tokenRequest new token from MDES/VTS