BudPay

Popular Searches:

Overview

This guide provides step-by-step instructions for integrating S2S V3 card payments with 3D Secure authentication.

Prerequisites

  • BudPay API credentials (Bearer token)
  • Cardinal Commerce setup
  • Backend API endpoints configured
  • Frontend iframe container for device data collection

Integration Flow

Step 1: Card Data Encryption

First, encrypt your card data using the test encryption endpoint:

curl --location 'https://backendapi.budpay.com/api/s2s/test/encryption' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxx' \
--data '{
   "data": {
       "number": "4000000000001091",
       "expiryMonth": "12",
       "expiryYear": "29",
       "cvv": "484"
   },
   "reference": "ref_21655737521418683"
}'

Expected Response:

740a75e2d40b04c29bca8ecd8fda00bceb3553a3dd31659d6f26c034cbb78c71bbf88bc31c3fc2d40669f3afa76e6f4fe517d38ab90d08cbd5d1cb64b20655db7c87a500e2edbd827c2c46931e7ce99b

Step 2: Transaction Initialization

Initialize the transaction with encrypted card data:

curl --location 'https://backendapi.budpay.com/api/s2s/transaction/initialize' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_live_gqyzln6pkbukqswylpcbcnr0diyygghe7mbeq5y' \
--data-raw '{
   "email": "test@email.com",
   "amount": "1",
   "currency": "USD",
   "reference": "ref_21655737521418683",
   "card" :"740a75e2d40b04c29bca8ecd8fda00bceb3553a3dd31659d6f26c034cbb78c71bbf88bc31c3fc2d40669f3afa76e6f4fe517d38ab90d08cbd5d1cb64b20655db7c87a500e2edbd827c2c46931e7ce99b"
}'

Expected Response:

{
   "status": true,
   "message": "Customer Authentication Successful",
   "data": {
       "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1MTU4ZTcwZS0zOWJhLTQwOGYtOTdjYS00ODY5ODJjMmNlYWUiLCJpYXQiOjE3NDcyMDcyODksImlzcyI6IjVkZDgzYmYwMGU0MjNkMTQ5OGRjYmFjYSIsImV4cCI6MTc0NzIxMDg4OSwiT3JnVW5pdElkIjoiNjgxNGE2ZDY3N2QxZWQ2YjNkMjMyZjVkIiwiUmVmZXJlbmNlSWQiOiI4ZmViNGU1NC0wZTllLTQxOWEtOTA5OC1kMDliNDEwZjgxYzUifQ.-1BtZuhnEKHRRQQY3KHZ0lJtkDMt82xjuPT9BrTfe4A",
       "deviceDataCollectionUrl": "https://centinelapistag.cardinalcommerce.com/V1/Cruise/Collect",
       "referenceId": "8feb4e54-0e9e-419a-9098-d09b410f81c5"
   }
}

Step 3: Device Data Collection (HTML/JS Implementation)

Create an iframe for Cardinal Commerce device fingerprinting:

<!DOCTYPE html>
<html>
<head>
    <title>Card Payment</title>
</head>
<body>
    <div class="cardinal-iframe-container" style="width: 100%; height: 400px;">
        <!-- Cardinal iframe will be injected here -->
    </div>

    <script>
        const performDeviceDataCollection = (reference, callback) => {
            // 1. Find iframe container
            const iFrameContainer = document.querySelector('.cardinal-iframe-container');
            
            if (!iFrameContainer) {
                console.error('Iframe container not found');
                callback();
                return;
            }

            // 2. Create iframe
            const iframe = document.createElement('iframe');
            iframe.src = `https://backendapi.budpay.com/cr6t76y8uijkny/authdevice/${reference}`;
            iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
            iframe.style.width = '100%';
            iframe.style.height = '100%';
            iframe.style.border = 'none';
            iframe.id = 'cardinal-collection-iframe';

            // 3. Listen for Cardinal events
            const messageHandler = (event) => {
                try {
                    if (event.data && event.data.MessageType === 'profile.completed') {
                        console.log('Cardinal profile completed');
                        window.removeEventListener('message', messageHandler);
                        clearTimeout(timeoutId);
                        callback();
                    }
                } catch (error) {
                    console.error('Error parsing Cardinal message:', error);
                }
            };

            // 4. Handle iframe load
            iframe.onload = () => {
                console.log('Cardinal iframe loaded successfully');
                window.addEventListener('message', messageHandler);
            };

            // 5. Handle loading errors
            iframe.onerror = () => {
                console.error('Failed to load Cardinal iframe');
                callback();
            };

            // 6. Add iframe to container
            iFrameContainer.appendChild(iframe);

            // 7. Fallback timeout (4.5 seconds)
            const timeoutId = setTimeout(() => {
                console.log('Cardinal timeout reached, proceeding...');
                window.removeEventListener('message', messageHandler);
                callback();
            }, 4500);
        };

        // Usage example:
        // Pass transaction reference used in initialize endpoint
        const reference = "ref_21655737521418683"; // From initialization response
        
        performDeviceDataCollection(reference, () => {
            console.log('Device data collection completed, proceed to enrollment check');
            // Proceed to Step 4
        });
    </script>
</body>
</html>

Step 4: Enrollment Check

After device data collection, check if 3D Secure authentication is required:

curl --location 'https://backendapi.budpay.com/api/cr6t76y8uijkny-enrollmentcheck' \
--header 'Content-Type: application/json' \
--data '{
    "cardnumber": "4000000000001091",
    "expiryMonth": "12",
    "expiryYear": "29",
    "cvv": "484",
    "ref": "ref_21655737521418683"
}'

Expected Response :

{
    "status": true,
    "message": "Proceed Payer Authentication",
    "data": {
        "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxOTAyNWJjMC03YmVhLTQzMmMtOTY0MS0wZDRhMzFjNGQzNDUiLCJpYXQiOjE3NDk3OTczMDcsImlzcyI6IjVlMjIwMDVmMzU2ZGNlMDNmMGY3ODcyZiIsImV4cCI6MTc0OTgwMDkwNywiT3JnVW5pdElkIjoiNjg0OWEzZTg5YWU5NjYzNTIyOGZmOWU3IiwiUGF5bG9hZCI6eyJBQ1NVcmwiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmNhcmRpbmFsY29tbWVyY2UuY29tL1RocmVlRFNlY3VyZS9WMl8xXzAvQ1JlcSIsIlBheWxvYWQiOiJleUp0WlhOellXZGxWSGx3WlNJNklrTlNaWEVpTENKdFpYTnpZV2RsVm1WeWMybHZiaUk2SWpJdU1pNHdJaXdpZEdoeVpXVkVVMU5sY25abGNsUnlZVzV6U1VRaU9pSXpOVFEwWlRZeFlTMWtaVFZsTFRRek5Ea3RZams0WVMwelptTTNNbVl6WVRRNFltUWlMQ0poWTNOVWNtRnVjMGxFSWpvaU1XVmpaR0V3T1RNdFpUVXhNaTAwTjJNMUxUbGlZamN0TkdVME5qaGhZakZtWTJZNUlpd2lZMmhoYkd4bGJtZGxWMmx1Wkc5M1UybDZaU0k2SWpBeUluMCIsIlRyYW5zYWN0aW9uSWQiOiI5TVgxbXNad09CS2ZnTHRLSmlMMSJ9LCJPYmplY3RpZnlQYXlsb2FkIjp0cnVlLCJSZXR1cm5VcmwiOiJodHRwczovL2JhY2tlbmRhcGkuYnVkcGF5LmNvbS9hcGkvdGVzdC1jeWJlcnNvdXJjZS1yZXR1cm51cmwifQ.lYAnc-tTXZahxPWaVf0FFeilmOa2SuZg9LGAmTkWx_Y",
        "stepUpUrl": "https://centinelapi.cardinalcommerce.com/V2/Cruise/StepUp"
    },
    "alt": "https://backendapi.budpay.com/cr6t76y8uijkny/authpayer/visa_june_12_2025_002"
}

    

Step 5: 3D Secure Authentication (if required)

If enrollment check returns a redirect URL, open authentication window:

const handle3DSAuthentication = (redirectUrl) => {
    if (redirectUrl) {
        // Open 3DS authentication window
        const authWindow = window.open(redirectUrl, '_blank');
        
        // Start monitoring payment status
        checkPaymentStatus(authWindow);
    } else {
        // No 3DS required, proceed to check final status
        checkPaymentStatus();
    }
};

Step 6: Payment Status Monitoring

Poll the transaction status until completion:

# Check transaction status
curl --location 'https://backendapi.budpay.com/api/verify-transaction/ref_21655737521418683'

Expected Response:

{
    "data": {
        "status": "success", // or "failed" or "pending"
        "message": "Transaction successful"
    }
}

Complete Integration Example

Backend Integration (using cURL)

#!/bin/bash

# Step 1: Encrypt card data
ENCRYPTED_CARD=$(curl -s --location 'https://backendapi.budpay.com/api/s2s/test/encryption' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
   "data": {
       "number": "4000000000001091",
       "expiryMonth": "12",
       "expiryYear": "29",
       "cvv": "484"
   },
   "reference": "ref_21655737521418683"
}')

echo "Encrypted card: $ENCRYPTED_CARD"

# Step 2: Initialize transaction
INIT_RESPONSE=$(curl -s --location 'https://backendapi.budpay.com/api/s2s/transaction/initialize' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data-raw '{
   "email": "test@email.com",
   "amount": "1",
   "currency": "USD",
   "reference": "ref_21655737521418683",
   "card": "'$ENCRYPTED_CARD'"
}')

echo "Init response: $INIT_RESPONSE"

# Extract reference used in transaction initialization for frontend use
REFERENCE=$(echo $INIT_RESPONSE | jq -r '.reference')
echo "Reference ID for frontend: $REFERENCE"

Frontend Integration (HTML/JS)

<!DOCTYPE html>
<html>
<head>
    <title>Cybersource Payment Integration</title>
</head>
<body>
    <h1>Payment Processing</h1>
    <div id="status">Initializing payment...</div>
    <div class="cardinal-iframe-container" style="width: 100%; height: 400px; border: 1px solid #ccc;">
        <!-- Cardinal iframe will be injected here -->
    </div>

    <script>
        // Configuration
        const REFERENCE = "8feb4e54-0e9e-419a-9098-d09b410f81c5"; // From backend initialization
        const reference = "ref_21655737521418683";

        // Update status display
        const updateStatus = (message) => {
            document.getElementById('status').textContent = message;
        };

        // Device data collection
        const performDeviceDataCollection = (reference, callback) => {
            updateStatus('Collecting device data...');
            
            const iFrameContainer = document.querySelector('.cardinal-iframe-container');
            if (!iFrameContainer) {
                console.error('Iframe container not found');
                callback();
                return;
            }

            const iframe = document.createElement('iframe');
            iframe.src = `https://backendapi.budpay.com/cr6t76y8uijkny/authdevice/${reference}`;
            iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
            iframe.style.width = '100%';
            iframe.style.height = '100%';
            iframe.style.border = 'none';

            const messageHandler = (event) => {
                try {
                    if (event.data && event.data.MessageType === 'profile.completed') {
                        console.log('Cardinal profile completed');
                        window.removeEventListener('message', messageHandler);
                        clearTimeout(timeoutId);
                        callback();
                    }
                } catch (error) {
                    console.error('Error parsing Cardinal message:', error);
                }
            };

            iframe.onload = () => {
                console.log('Cardinal iframe loaded');
                window.addEventListener('message', messageHandler);
            };

            iframe.onerror = () => {
                console.error('Cardinal iframe failed to load');
                callback();
            };

            iFrameContainer.appendChild(iframe);

            const timeoutId = setTimeout(() => {
                console.log('Cardinal timeout reached');
                window.removeEventListener('message', messageHandler);
                callback();
            }, 4500);
        };

        // Check payment status
        const checkPaymentStatus = async (authWindow = null) => {
            try {
                const response = await fetch(`https://backendapi.budpay.com/api/verify-transaction/${reference}`);
                const data = await response.json();
                const status = data.data?.status;

                switch (status) {
                    case 'pending':
                        updateStatus('Payment pending, checking again...');
                        setTimeout(() => checkPaymentStatus(authWindow), 3000);
                        break;
                        
                    case 'success':
                        updateStatus('Payment successful!');
                        if (authWindow) authWindow.close();
                        break;
                        
                    case 'failed':
                        updateStatus('Payment failed!');
                        if (authWindow) authWindow.close();
                        break;
                }
            } catch (error) {
                console.error('Error checking status:', error);
                updateStatus('Error checking payment status');
            }
        };

        // Start the process
        performDeviceDataCollection(reference, () => {
            updateStatus('Device data collection completed. Processing payment...');
            
            // In a real implementation, you would make the enrollment check here
            // For this example, we'll proceed directly to status checking
            checkPaymentStatus();
        });
    </script>
</body>
</html>

Required API Endpoints

1. Card Encryption

POST https://backendapi.budpay.com/api/s2s/test/encryption
Authorization: Bearer your_api_key

2. Transaction Initialization

POST https://backendapi.budpay.com/api/s2s/transaction/initialize
Authorization: Bearer your_api_key

3. Device Authentication (Frontend Only)

GET https://backendapi.budpay.com/cr6t76y8uijkny/authdevice/{referenceId}

4. Enrollment Check

POST https://backendapi.budpay.com/api/cr6t76y8uijkny-enrollmentcheck

5. Transaction Verification

GET https://backendapi.budpay.com/api/verify-transaction/{reference}

Error Handling

Backend Error Handling (cURL)

# Check HTTP status codes
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" 'https://backendapi.budpay.com/api/s2s/test/encryption' \
--header 'Authorization: Bearer invalid_key')

if [ $HTTP_STATUS -ne 200 ]; then
    echo "Error: HTTP $HTTP_STATUS"
    cat response.json
fi

Frontend Error Handling

// Handle iframe errors
iframe.onerror = () => {
    console.error('Cardinal iframe failed to load');
    updateStatus('Error loading payment processor');
    callback(); // Continue with fallback
};

// Handle API errors
const checkPaymentStatus = async (authWindow = null) => {
    try {
        const response = await fetch(`https://backendapi.budpay.com/api/verify-transaction/${TRANSACTION_REF}`);
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
        
        const data = await response.json();
        // ... rest of status handling
    } catch (error) {
        console.error('Status check failed:', error);
        updateStatus('Error checking payment status');
        // Retry after delay
        setTimeout(() => checkPaymentStatus(authWindow), 5000);
    }
};

Security Considerations

  1. API Key Security: Store API keys securely, never expose in frontend code
  2. Card Data Encryption: Always encrypt card data before transmission
  3. Iframe Sandboxing: Use proper sandbox attributes for Cardinal iframe
  4. HTTPS Required: All API calls must be over HTTPS

Implementation Checklist

  • [ ] API credentials configured
  • [ ] Card encryption endpoint working (cURL)
  • [ ] Transaction initialization working (cURL)
  • [ ] HTML page with iframe container ready
  • [ ] Cardinal device collection implemented (JS)
  • [ ] Enrollment check integration (cURL)
  • [ ] 3DS window management (JS)
  • [ ] Status polling implemented (JS)
  • [ ] Error handling added
  • [ ] Testing completed

This integration uses cURL for secure backend operations and HTML/JavaScript only for the Cardinal Commerce device data collection step, providing a secure and reliable payment processing flow.