Payment Gateway API Documentation

Complete integration guide for our Payment Gateway API supporting secure payment processing with full 3D Secure authentication.

Key Features

  • Full 3D Secure (3DS) authentication support
  • Automatic payment method selection
  • Flexible address handling
  • Real-time transaction status
  • Secure API authentication
  • Multi-currency support

Authentication

All API requests must include your API credentials in the request headers:

X-Api-Key: your_api_key_here
X-Api-Secret: your_api_secret_here
Content-Type: application/json
Security: Never expose your API credentials in client-side code. All API calls should be made from your secure server environment.

Payment Processing

Our gateway automatically handles payment method selection and routing based on your account configuration. You don't need to specify payment providers - the system will use the optimal method for each transaction.

Feature Support Description
Credit Cards ✅ All Major Cards Visa, Mastercard, American Express, Discover
3D Secure ✅ Full 3DS2 Automatic fraud protection and authentication
Multi-Currency ✅ Global Support USD, EUR, GBP, CAD, AUD, JPY, SGD and more
Tokenization ✅ Secure Storage PCI-compliant card tokenization

Address Handling

Proper billing address information is crucial for payment processing and 3DS authentication. The API supports flexible address formats:

Single String Format (Recommended)

{
    "billing_address": "123 Main Street, New York, NY, 10001, US"
}

Individual Components Format

{
    "address_line": "123 Main Street",
    "city": "New York", 
    "state": "NY",
    "postal_code": "10001",
    "country": "US"
}

Address Requirements

Component Required Description
Street Address Required Full street address including number and street name
City Required City or locality name
State/Province Optional State, province, or administrative area
Postal Code Optional ZIP/postal code (recommended for better approval rates)
Country Required 2-letter ISO country code (US, GB, CA, etc.)
Important: Placeholder values like "N/A" or empty strings will be rejected. Provide complete, accurate address information for best results.

Initialize Payment

Create a new payment transaction. The system automatically selects the optimal payment method based on your account configuration.

Endpoint:

POST https://gateway.pesogate.com/api/v1/payments/initialize

Parameters:

Parameter Type Required Description
order_id string Required Your unique order identifier
amount decimal Required Payment amount (e.g., 100.50)
currency string Required 3-letter currency code (USD, EUR, GBP)
card_number string Required* Card number (16-19 digits)
exp_month string Required* Expiration month (01-12)
exp_year string Required* Expiration year (YY or YYYY)
cvv string Required* Card verification value (3-4 digits)
payment_source_token string Optional** Previously tokenized payment source
customer_name string Optional Cardholder name
customer_email string Optional Customer email address
billing_address string Required Complete billing address (see Address Handling section)
return_url string Required URL for successful payment redirect
cancel_url string Required URL for cancelled/failed payment redirect

* Required when using direct card payment

** Use payment_source_token OR card details, not both

Example Request:

curl -X POST "https://gateway.pesogate.com/api/v1/payments/initialize" \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: your_api_key" \
    -H "X-Api-Secret: your_api_secret" \
    -d '{
        "order_id": "ORDER123456",
        "amount": 100.50,
        "currency": "USD",
        "card_number": "4111111111111111",
        "exp_month": "12",
        "exp_year": "25",
        "cvv": "123",
        "customer_name": "John Doe",
        "customer_email": "john.doe@example.com",
        "billing_address": "123 Main Street, New York, NY, 10001, US",
        "return_url": "https://your-website.com/payment/success",
        "cancel_url": "https://your-website.com/payment/cancel"
    }'

Response Types:

1. Standard Redirect Response:

{
    "success": true,
    "action": "redirect",
    "message": "Transaction initialized successfully",
    "redirect_url": "https://checkout.gateway.com/pay/abc123def456",
    "transaction_reference": "550e8400-e29b-41d4-a716-446655440000"
}

2. 3DS Challenge Response:

{
    "success": true,
    "action": "render_3ds_fullscreen",
    "html_content": "<html>...3DS challenge form...</html>",
    "message": "Secure authentication required",
    "transaction_reference": "550e8400-e29b-41d4-a716-446655440000"
}

3. Error Response:

{
    "success": false,
    "message": "Validation failed",
    "errors": {
        "billing_address": ["Street address is required"],
        "card_number": ["Invalid card number"]
    },
    "error_code": "VALIDATION_ERROR"
}

3DS Challenge Implementation

When the API returns action: "render_3ds_fullscreen", you must render the 3DS authentication challenge. Here's how to implement it properly:

Critical: The 3DS challenge must be rendered in a secure iframe or popup window to maintain PCI compliance and security standards.

PHP Implementation

<?php
function handlePaymentResponse($apiResponse) {
    $result = json_decode($apiResponse, true);
    
    if ($result['success'] && isset($result['action'])) {
        switch ($result['action']) {
            case 'render_3ds_fullscreen':
                render3DSChallenge($result['html_content'], $result['transaction_reference']);
                break;
                
            case 'redirect':
                header("Location: " . $result['redirect_url']);
                exit;
        }
    } else {
        // Handle error
        handlePaymentError($result);
    }
}

function render3DSChallenge($htmlContent, $transactionRef) {
    // Store transaction reference for callback handling
    $_SESSION['transaction_reference'] = $transactionRef;
    
    // Clean and validate HTML content
    $cleanHtml = html_entity_decode($htmlContent, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    
    echo '<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Secure Payment Authentication</title>
    <style>
        body { 
            margin: 0; 
            padding: 20px; 
            font-family: Arial, sans-serif;
            background: #f5f5f5;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        .auth-container {
            background: white;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            padding: 20px;
            max-width: 500px;
            width: 100%;
        }
        .auth-frame {
            width: 100%;
            min-height: 400px;
            border: none;
            border-radius: 4px;
        }
        .loading {
            text-align: center;
            color: #666;
            padding: 20px;
        }
    </style>
</head>
<body>
    <div class="auth-container">
        <div class="loading" id="loading">
            <p>Loading secure authentication...</p>
        </div>
        <iframe 
            id="auth-frame" 
            class="auth-frame" 
            style="display: none;"
            sandbox="allow-forms allow-scripts allow-same-origin allow-top-navigation"
            onload="document.getElementById(\'loading\').style.display=\'none\'; this.style.display=\'block\';"
        ></iframe>
    </div>
    
    <script>
        const iframe = document.getElementById("auth-frame");
        const htmlContent = ' . json_encode($cleanHtml) . ';
        
        // Create blob URL for secure content loading
        const blob = new Blob([htmlContent], { type: "text/html" });
        const url = URL.createObjectURL(blob);
        iframe.src = url;
        
        // Clean up blob URL after loading
        iframe.onload = function() {
            setTimeout(() => URL.revokeObjectURL(url), 1000);
        };
    </script>
</body>
</html>';
    exit;
}
?>

JavaScript/AJAX Implementation

async function initializePayment(paymentData) {
    try {
        const response = await fetch('/api/v1/payments/initialize', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Api-Key': 'your_api_key',
                'X-Api-Secret': 'your_api_secret'
            },
            body: JSON.stringify(paymentData)
        });
        
        const result = await response.json();
        
        if (result.success) {
            switch (result.action) {
                case 'render_3ds_fullscreen':
                    render3DSModal(result.html_content, result.transaction_reference);
                    break;
                    
                case 'redirect':
                    window.location.href = result.redirect_url;
                    break;
            }
        } else {
            handlePaymentError(result);
        }
    } catch (error) {
        console.error('Payment initialization failed:', error);
        showErrorMessage('Network error occurred. Please try again.');
    }
}

function render3DSModal(htmlContent, transactionRef) {
    // Create modal overlay
    const modal = document.createElement('div');
    modal.id = 'threeds-modal';
    modal.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0,0,0,0.8);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10000;
    `;
    
    // Create container
    const container = document.createElement('div');
    container.style.cssText = `
        background: white;
        border-radius: 8px;
        padding: 20px;
        max-width: 500px;
        width: 90%;
        max-height: 80vh;
        overflow: hidden;
        position: relative;
    `;
    
    // Create close button
    const closeBtn = document.createElement('button');
    closeBtn.innerHTML = '×';
    closeBtn.style.cssText = `
        position: absolute;
        top: 10px;
        right: 15px;
        background: none;
        border: none;
        font-size: 24px;
        cursor: pointer;
        color: #666;
    `;
    closeBtn.onclick = () => {
        document.body.removeChild(modal);
        // Optionally redirect to cancel URL
        window.location.href = paymentData.cancel_url;
    };
    
    // Create iframe for 3DS challenge
    const iframe = document.createElement('iframe');
    iframe.style.cssText = `
        width: 100%;
        height: 400px;
        border: none;
        border-radius: 4px;
    `;
    iframe.sandbox = 'allow-forms allow-scripts allow-same-origin allow-top-navigation';
    
    // Store transaction reference for callback handling
    sessionStorage.setItem('transaction_reference', transactionRef);
    
    // Inject HTML content securely
    const blob = new Blob([htmlContent], { type: 'text/html' });
    const url = URL.createObjectURL(blob);
    iframe.src = url;
    
    container.appendChild(closeBtn);
    container.appendChild(iframe);
    modal.appendChild(container);
    document.body.appendChild(modal);
    
    // Clean up after iframe loads
    iframe.onload = () => {
        setTimeout(() => URL.revokeObjectURL(url), 1000);
    };
    
    // Listen for completion messages (optional)
    window.addEventListener('message', function(event) {
        if (event.data.type === '3ds_complete') {
            document.body.removeChild(modal);
            // Check transaction status or redirect
            checkTransactionStatus(transactionRef);
        }
    });
}
Best Practices:
  • Always use iframe sandbox attributes for security
  • Implement proper loading states for better UX
  • Store transaction reference for status checking
  • Provide clear error handling and retry options
  • Test with both challenge and frictionless flows

Check Transaction Status

Retrieve the current status and details of a transaction.

Endpoint:

GET https://gateway.pesogate.com/api/v1/payments/{reference}/status

Example Request:

curl -X GET "https://gateway.pesogate.com/api/v1/payments/550e8400-e29b-41d4-a716-446655440000/status" \
    -H "X-Api-Key: your_api_key" \
    -H "X-Api-Secret: your_api_secret"

Response:

{
    "success": true,
    "message": "Transaction status retrieved",
    "data": {
        "reference": "550e8400-e29b-41d4-a716-446655440000",
        "order_id": "ORDER123456",
        "status": "completed",
        "amount": 100.50,
        "currency": "USD",
        "payment_method": "card",
        "card_type": "visa",
        "card_last_four": "1111",
        "customer_email": "john.doe@example.com",
        "created_at": "2023-12-01T10:30:00Z",
        "completed_at": "2023-12-01T10:31:45Z",
        "3ds_authenticated": true
    }
}

Transaction Statuses

Status Description Next Steps
initialized Transaction created, pending processing Wait for completion or check again
processing Payment is being processed Wait for completion
completed Payment successfully completed Fulfill order/service
failed Payment failed or was declined Retry with different card or method
cancelled Payment was cancelled by user Offer alternative payment method
refunded Payment has been refunded Update order status accordingly

Refund Transaction

Process a full or partial refund for a completed transaction.

Endpoint:

POST https://gateway.pesogate.com/api/v1/payments/{reference}/refund

Parameters:

Parameter Type Required Description
amount decimal Optional Refund amount (defaults to full amount)
reason string Optional Reason for refund

Example Request:

curl -X POST "https://gateway.pesogate.com/api/v1/payments/550e8400-e29b-41d4-a716-446655440000/refund" \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: your_api_key" \
    -H "X-Api-Secret: your_api_secret" \
    -d '{
        "amount": 50.25,
        "reason": "Partial refund - item returned"
    }'

Response:

{
    "success": true,
    "message": "Refund processed successfully",
    "data": {
        "refund_id": "ref_550e8400-e29b-41d4-a716-446655440000",
        "original_reference": "550e8400-e29b-41d4-a716-446655440000",
        "refund_amount": 50.25,
        "currency": "USD",
        "status": "completed",
        "processed_at": "2023-12-01T15:30:00Z"
    }
}

Error Handling

The API uses standard HTTP status codes and provides detailed error information in the response body.

Common Error Codes

HTTP Status Error Code Description
400 BAD_REQUEST Invalid request format or parameters
401 UNAUTHORIZED Invalid or missing API credentials
422 VALIDATION_ERROR Request validation failed
429 RATE_LIMIT_EXCEEDED Too many requests - rate limit exceeded
500 INTERNAL_ERROR Internal server error

3DS Specific Errors

{
    "success": false,
    "message": "Authentication required",
    "error_code": "3DS_AUTH_REQUIRED",
    "details": {
        "reason": "Card authentication required by issuer",
        "recommended_action": "complete_3ds_challenge"
    }
}

Address Validation Errors

{
    "success": false,
    "message": "Address validation failed",
    "error_code": "ADDRESS_VALIDATION_ERROR",
    "errors": {
        "billing_address": [
            "Street address is required",
            "Country must be a valid 2-letter code"
        ]
    }
}

Testing Your Integration

Test your integration using real credit cards with small amounts in the live environment. This ensures accurate testing of the complete payment flow including 3DS authentication and all security features.

Live Environment Testing Benefits

  • Test actual 3DS authentication flows as implemented by card issuers
  • Verify real-world payment processing scenarios
  • Ensure compatibility with all payment networks
  • Test actual fraud detection and security measures
  • Validate address verification and CVV checking
  • Experience the complete customer payment journey

Testing Best Practices

Recommended Test Amounts

  • $1.00 USD - Basic payment flow testing
  • $2.00 USD - 3DS challenge testing
  • $5.00 USD - Refund functionality testing
  • €1.00 EUR - Multi-currency testing
  • £1.00 GBP - International payment testing

These small amounts minimize costs while providing comprehensive testing coverage.

Testing Scenarios

Test Scenario Amount Card Type Expected Outcome
Basic Payment $1.00 Your personal card Successful payment or 3DS challenge
3DS Authentication $2.00 Personal card 3DS challenge flow
International Card $1.00 Non-domestic card Cross-border processing
Different Currency €1.00 EUR-enabled card Multi-currency handling
Refund Testing $5.00 Personal card Full and partial refunds

Test Billing Addresses

// Valid US Address
"billing_address": "123 Main Street, New York, NY, 10001, US"

// Valid UK Address  
"billing_address": "10 Downing Street, London, SW1A 2AA, GB"

// Valid Canadian Address
"billing_address": "100 Wellington Street, Ottawa, ON, K1A 0A2, CA"

// Valid Australian Address
"billing_address": "1 Martin Place, Sydney, NSW, 2000, AU"

Testing Checklist

Before Going Live

Testing Tips

  • Use valid billing addresses that match your card's registered address
  • Test with cards from different banks and countries when possible
  • Verify that declined payments are handled gracefully
  • Test the complete customer journey from payment to receipt
  • Document any issues and retest after fixes
  • Keep transaction receipts for reconciliation
Why Live Testing? Real-world testing with actual payment networks provides the most accurate validation of your integration. Sandbox environments cannot replicate the complexity of actual card issuer responses, 3DS challenges, and fraud detection systems that your customers will experience.

Complete Code Examples

Complete PHP Integration

<?php
class PaymentGateway {
    private $apiKey;
    private $apiSecret;
    private $baseUrl;
    
    public function __construct($apiKey, $apiSecret) {
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
        $this->baseUrl = 'https://api.yourgateway.com/v1';
    }
    
    public function initializePayment($paymentData) {
        $url = $this->baseUrl . '/payments/initialize';
        
        $headers = [
            'Content-Type: application/json',
            'X-Api-Key: ' . $this->apiKey,
            'X-Api-Secret: ' . $this->apiSecret
        ];
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($paymentData));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode !== 200) {
            throw new Exception('API request failed with HTTP code: ' . $httpCode);
        }
        
        return json_decode($response, true);
    }
    
    public function checkTransactionStatus($reference) {
        $url = $this->baseUrl . '/payments/' . $reference . '/status';
        
        $headers = [
            'X-Api-Key: ' . $this->apiKey,
            'X-Api-Secret: ' . $this->apiSecret
        ];
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        return json_decode($response, true);
    }
}

// Usage example for live testing
try {
    $gateway = new PaymentGateway('your_api_key', 'your_api_secret');
    
    $paymentData = [
        'order_id' => 'TEST-ORDER-' . time(),
        'amount' => 1.00, // Test with small real amounts
        'currency' => 'USD',
        'card_number' => '4111111111111111', // Use your own card for testing
        'exp_month' => '12',
        'exp_year' => '25',
        'cvv' => '123',
        'customer_name' => 'John Doe',
        'customer_email' => 'john.doe@example.com',
        'billing_address' => '123 Main Street, New York, NY, 10001, US',
        'return_url' => 'https://your-website.com/success',
        'cancel_url' => 'https://your-website.com/cancel'
    ];
    
    $result = $gateway->initializePayment($paymentData);
    
    if ($result['success']) {
        switch ($result['action']) {
            case 'render_3ds_fullscreen':
                // Handle 3DS challenge
                render3DSChallenge($result['html_content']);
                break;
                
            case 'redirect':
                // Redirect to hosted checkout
                header('Location: ' . $result['redirect_url']);
                exit;
        }
    } else {
        echo 'Payment failed: ' . $result['message'];
    }
    
} catch (Exception $e) {
    echo 'Error: ' . $e->getMessage();
}

function render3DSChallenge($htmlContent) {
    // Implementation as shown in 3DS section above
    // ...
}
?>

HTML Payment Form Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Secure Payment Gateway</title>
    <style>
        .payment-form {
            max-width: 500px;
            margin: 50px auto;
            padding: 30px;
            border: 1px solid #ddd;
            border-radius: 8px;
            font-family: Arial, sans-serif;
        }
        .form-group {
            margin-bottom: 20px;
        }
        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #333;
        }
        .form-group input, .form-group select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }
        .form-row {
            display: flex;
            gap: 15px;
        }
        .form-row .form-group {
            flex: 1;
        }
        .submit-btn {
            background: #007cba;
            color: white;
            padding: 12px 30px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            width: 100%;
        }
        .submit-btn:hover {
            background: #005a87;
        }
        .submit-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        .required {
            color: #e74c3c;
        }
        .help-text {
            font-size: 12px;
            color: #666;
            margin-top: 5px;
        }
    </style>
</head>
<body>
    <form id="payment-form" class="payment-form">
        <h2>Secure Payment</h2>
        
        <div class="form-group">
            <label>Amount <span class="required">*</span></label>
            <input type="number" name="amount" step="0.01" min="0.01" required value="1.00">
            <div class="help-text">Test with small real amounts for best results</div>
        </div>
        
        <div class="form-group">
            <label>Currency <span class="required">*</span></label>
            <select name="currency" required>
                <option value="USD">USD - US Dollar</option>
                <option value="EUR">EUR - Euro</option>
                <option value="GBP">GBP - British Pound</option>
            </select>
        </div>
        
        <div class="form-group">
            <label>Card Number <span class="required">*</span></label>
            <input type="text" name="card_number" required maxlength="19" 
                   placeholder="1234 5678 9012 3456"
                   oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/(.{4})/g, '$1 ').trim()">
        </div>
        
        <div class="form-row">
            <div class="form-group">
                <label>Month <span class="required">*</span></label>
                <select name="exp_month" required>
                    <option value="">MM</option>
                    <option value="01">01</option>
                    <option value="02">02</option>
                    <option value="03">03</option>
                    <option value="04">04</option>
                    <option value="05">05</option>
                    <option value="06">06</option>
                    <option value="07">07</option>
                    <option value="08">08</option>
                    <option value="09">09</option>
                    <option value="10">10</option>
                    <option value="11">11</option>
                    <option value="12">12</option>
                </select>
            </div>
            <div class="form-group">
                <label>Year <span class="required">*</span></label>
                <select name="exp_year" required>
                    <option value="">YYYY</option>
                    <option value="25">2025</option>
                    <option value="26">2026</option>
                    <option value="27">2027</option>
                    <option value="28">2028</option>
                    <option value="29">2029</option>
                    <option value="30">2030</option>
                </select>
            </div>
            <div class="form-group">
                <label>CVV <span class="required">*</span></label>
                <input type="text" name="cvv" required maxlength="4" placeholder="123"
                       oninput="this.value = this.value.replace(/[^0-9]/g, '')">
            </div>
        </div>
        
        <div class="form-group">
            <label>Cardholder Name <span class="required">*</span></label>
            <input type="text" name="customer_name" required placeholder="John Doe">
        </div>
        
        <div class="form-group">
            <label>Email Address</label>
            <input type="email" name="customer_email" placeholder="john.doe@example.com">
            <div class="help-text">Optional: For transaction receipts</div>
        </div>
        
        <div class="form-group">
            <label>Billing Address <span class="required">*</span></label>
            <input type="text" name="billing_address" required 
                   placeholder="123 Main Street, New York, NY, 10001, US">
            <div class="help-text">Format: Street, City, State, Postal Code, Country</div>
        </div>
        
        <button type="submit" class="submit-btn" id="submit-btn">
            Process Secure Payment
        </button>
        
        <div class="help-text" style="text-align: center; margin-top: 15px;">
            Your payment is processed securely with bank-level encryption
        </div>
    </form>
    
    <script>
        // Add your payment gateway JavaScript here
        // Implementation details in the JavaScript examples above
    </script>
</body>
</html>

Integration Checklist

Before Going Live:

Support and Resources

Need help with your integration? Here are your support options:

Technical Support

Email: support@gateway.com

Response time: Within 24 hours

Documentation

This guide covers most integration scenarios

Check our detailed integration guide