<?php
/**
* Test API for PagoMovil Bank List - FINAL FIXED VERSION
* Fixed UTF-8 encoding issues and proper bank list display
*/
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
class PagoMovilBankTest {
private $api_base_url = 'https://servicios.bncenlinea.com:16500';
private $client_guid = '8358c7d5-5a6c-4883-8526-05ec48ed0130';
private $master_key = '667ce71bfc380183546d0005140b58f3';
private $working_key = '';
public function __construct() {
echo "<!DOCTYPE html>
<html>
<head>
<title>PagoMovil Bank List Test - FIXED</title>
<style>
body { font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
.success { color: green; background: #f0fff0; padding: 10px; border-left: 4px solid green; }
.error { color: red; background: #fff0f0; padding: 10px; border-left: 4px solid red; }
.info { background: #f0f8ff; padding: 10px; border-left: 4px solid #0073aa; margin: 10px 0; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background-color: #f2f2f2; }
.bank-dropdown { width: 100%; padding: 8px; margin: 10px 0; }
</style>
</head>
<body>";
echo "<h1>PagoMovil Bank List API Test - FINAL FIXED VERSION</h1>\n";
echo "<p><strong>Base URL:</strong> " . $this->api_base_url . "</p>\n";
echo "<p><strong>Client GUID:</strong> " . substr($this->client_guid, 0, 8) . "...</p>\n";
}
public function runTests() {
echo "<h2>Step 1: Authentication</h2>\n";
if (!$this->authenticate()) {
echo "<p class='error'><strong>Authentication FAILED</strong></p>\n";
return false;
}
echo "<h2>Step 2: Get Bank List</h2>\n";
$banks = $this->getBankList();
if ($banks && is_array($banks)) {
echo "<p class='success'><strong>Bank list retrieved successfully!</strong></p>\n";
$this->displayBanks($banks);
$this->displayBankDropdown($banks);
return true;
} else {
echo "<p class='error'><strong>Failed to get bank list</strong></p>\n";
return false;
}
}
private function authenticate() {
echo "<p>Authenticating with bank API...</p>\n";
$auth_data = array('ClientGUID' => $this->client_guid);
$encrypted_data = $this->encrypt_data($auth_data, $this->master_key);
if (!$encrypted_data) {
echo "<p class='error'>Encryption failed</p>\n";
return false;
}
$response = $this->make_api_call('/api/Auth/LogOn', $encrypted_data);
if ($response && isset($response['status']) && $response['status'] === 'OK') {
$decrypted_response = $this->decrypt_data($response['value'], $this->master_key);
if ($decrypted_response === false) {
echo "<p class='error'>Decryption failed</p>\n";
return false;
}
echo "<div class='info'><strong>Decrypted authentication response:</strong> " . htmlspecialchars($decrypted_response) . "</div>\n";
$response_data = json_decode($decrypted_response, true);
if (isset($response_data['WorkingKey'])) {
$this->working_key = $response_data['WorkingKey'];
echo "<p class='success'><strong>Authentication SUCCESSFUL</strong></p>\n";
echo "<p>WorkingKey obtained: " . substr($this->working_key, 0, 20) . "...</p>\n";
return true;
} else {
echo "<p class='error'>WorkingKey not found in response</p>\n";
if (isset($response_data['Message'])) {
echo "<p>API Message: " . htmlspecialchars($response_data['Message']) . "</p>\n";
}
}
} else {
echo "<p class='error'>Authentication failed: API status not OK</p>\n";
if (isset($response['message'])) {
echo "<p>Error message: " . htmlspecialchars($response['message']) . "</p>\n";
}
}
return false;
}
private function getBankList() {
echo "<p>Requesting bank list from API...</p>\n";
if (empty($this->working_key)) {
echo "<p class='error'>No WorkingKey available. Please authenticate first.</p>\n";
return false;
}
// Use empty object format which worked in your test
$bank_data = new stdClass();
$encrypted_data = $this->encrypt_data($bank_data, $this->working_key);
if (!$encrypted_data) {
echo "<p class='error'>Bank data encryption failed</p>\n";
return false;
}
$response = $this->make_api_call('/api/Services/Banks', $encrypted_data);
if ($response && isset($response['status']) && $response['status'] === 'OK') {
$decrypted_response = $this->decrypt_data($response['value'], $this->working_key);
if ($decrypted_response === false) {
echo "<p class='error'>Bank list decryption failed</p>\n";
return false;
}
echo "<div class='info'>";
echo "<p><strong>Decrypted bank list response length:</strong> " . strlen($decrypted_response) . " characters</p>\n";
// FIX: Clean UTF-8 encoding before JSON decoding
$clean_response = $this->clean_utf8($decrypted_response);
// Try to decode JSON with error handling
$banks = json_decode($clean_response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "<p class='error'>JSON decode error: " . json_last_error_msg() . "</p>\n";
// Try to fix common JSON issues
$fixed_response = $this->fix_json($clean_response);
$banks = json_decode($fixed_response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "<p class='error'>Still cannot decode JSON after fixes. Showing raw data:</p>\n";
echo "<pre>" . htmlspecialchars($clean_response) . "</pre>\n";
return false;
}
}
echo "</div>";
return $banks;
} else {
echo "<p class='error'>Bank list API returned non-OK status</p>\n";
if (isset($response['message'])) {
echo "<p>Error message: " . htmlspecialchars($response['message']) . "</p>\n";
}
}
return false;
}
/**
* Clean UTF-8 encoding issues
*/
private function clean_utf8($string) {
// First, try to detect encoding and convert to UTF-8
$encoding = mb_detect_encoding($string, ['UTF-8', 'ISO-8859-1', 'Windows-1252'], true);
if ($encoding && $encoding != 'UTF-8') {
$string = mb_convert_encoding($string, 'UTF-8', $encoding);
}
// Remove any invalid UTF-8 characters
$string = mb_convert_encoding($string, 'UTF-8', 'UTF-8');
// Replace common problematic characters
$string = str_replace(
['á', 'é', 'Ã', 'ó', 'ú', 'ñ', 'Ã', 'É', 'Ã', 'Ó', 'Ú', 'Ñ'],
['á', 'é', 'í', 'ó', 'ú', 'ñ', 'Á', 'É', 'Í', 'Ó', 'Ú', 'Ñ'],
$string
);
return $string;
}
/**
* Fix common JSON formatting issues
*/
private function fix_json($json_string) {
// Remove BOM if present
$json_string = str_replace("\xEF\xBB\xBF", '', $json_string);
// Trim whitespace
$json_string = trim($json_string);
return $json_string;
}
private function make_api_call($endpoint, $data) {
$url = $this->api_base_url . $endpoint;
echo "<div class='info'>\n";
echo "<p><strong>API Request:</strong></p>\n";
echo "<p>Endpoint: " . htmlspecialchars($url) . "</p>\n";
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
],
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_FOLLOWLOCATION => true
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
echo "<p><strong>HTTP Response Code:</strong> " . $http_code . "</p>\n";
// HTTP 202 Accepted is a SUCCESS status
$success_codes = [200, 202];
if ($curl_error) {
echo "<p class='error'><strong>cURL Error:</strong> " . htmlspecialchars($curl_error) . "</p>\n";
echo "</div>\n";
return false;
}
if (!in_array($http_code, $success_codes)) {
echo "<p class='error'><strong>HTTP Error:</strong> " . $http_code . "</p>\n";
echo "<p><strong>Raw Response:</strong> " . htmlspecialchars($response) . "</p>\n";
echo "</div>\n";
return false;
}
echo "</div>\n";
$decoded_response = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "<p class='error'>JSON decode error: " . json_last_error_msg() . "</p>\n";
return false;
}
return $decoded_response;
}
private function encrypt_data($data, $key) {
$plain_text = json_encode($data);
$salt = hex2bin('4976616e204d65647665646576'); // "Ivan Medvedev"
// Use PBKDF2 to derive key and IV (exactly like Postman)
$derived_key = hash_pbkdf2('sha1', $key, $salt, 1000, 48, true);
$aes_key = substr($derived_key, 0, 32);
$iv = substr($derived_key, 32, 16);
// Convert to UTF-16LE (little endian) and encrypt
$plain_text_utf16 = '';
for ($i = 0; $i < strlen($plain_text); $i++) {
$plain_text_utf16 .= $plain_text[$i] . "\x00";
}
$cipher_text = openssl_encrypt(
$plain_text_utf16,
'AES-256-CBC',
$aes_key,
OPENSSL_RAW_DATA,
$iv
);
if ($cipher_text === false) {
echo "<p class='error'>Encryption failed: " . openssl_error_string() . "</p>\n";
return false;
}
$validation = hash('sha256', $plain_text);
return array(
'ClientGUID' => $this->client_guid,
'Reference' => 'REF' . time(),
'Value' => base64_encode($cipher_text),
'Validation' => $validation,
'swTestOperation' => false
);
}
private function decrypt_data($encrypted_data, $key) {
$salt = hex2bin('4976616e204d65647665646576');
$derived_key = hash_pbkdf2('sha1', $key, $salt, 1000, 48, true);
$aes_key = substr($derived_key, 0, 32);
$iv = substr($derived_key, 32, 16);
$decrypted = openssl_decrypt(
base64_decode($encrypted_data),
'AES-256-CBC',
$aes_key,
OPENSSL_RAW_DATA,
$iv
);
if ($decrypted === false) {
echo "<p class='error'>Decryption failed: " . openssl_error_string() . "</p>\n";
return false;
}
// Convert from UTF-16LE to UTF-8
$result = '';
for ($i = 0; $i < strlen($decrypted); $i += 2) {
if ($decrypted[$i] != "\x00") {
$result .= $decrypted[$i];
}
}
return $result;
}
private function displayBanks($banks) {
if (!is_array($banks) || empty($banks)) {
echo "<p>No banks found in response</p>\n";
return;
}
echo "<h3>Complete Bank List (" . count($banks) . " banks found)</h3>\n";
echo "<table>\n";
echo "<tr><th>Code</th><th>Bank Name</th><th>Services</th></tr>\n";
foreach ($banks as $bank) {
$code = htmlspecialchars($bank['Code'] ?? 'N/A');
$name = htmlspecialchars($bank['Name'] ?? 'N/A');
$services = htmlspecialchars($bank['Services'] ?? 'N/A');
echo "<tr>\n";
echo "<td><strong>{$code}</strong></td>\n";
echo "<td>{$name}</td>\n";
echo "<td>{$services}</td>\n";
echo "</tr>\n";
}
echo "</table>\n";
}
private function displayBankDropdown($banks) {
if (!is_array($banks) || empty($banks)) {
echo "<p>No banks available for dropdown</p>\n";
return;
}
echo "<h3>Bank Selection Dropdown</h3>\n";
echo "<select class='bank-dropdown' onchange='showSelectedBank(this)'>\n";
echo "<option value=''>-- Select Your Bank --</option>\n";
foreach ($banks as $bank) {
$code = htmlspecialchars($bank['Code'] ?? '');
$name = htmlspecialchars($bank['Name'] ?? '');
$services = htmlspecialchars($bank['Services'] ?? '');
$displayText = "{$code}: {$name}";
if (!empty($services)) {
$displayText .= " ({$services})";
}
$value = "{$code}|{$name}";
echo "<option value='{$value}'>{$displayText}</option>\n";
}
echo "</select>\n";
echo "
<script>
function showSelectedBank(select) {
if(select.value) {
const [code, name] = select.value.split('|');
alert('Selected Bank: ' + code + ' - ' + name);
}
}
</script>
";
// Also show BNC bank specifically if found
$bnc_found = false;
foreach ($banks as $bank) {
if (($bank['Code'] ?? '') === '0191') {
$bnc_found = true;
echo "<div class='success'>\n";
echo "<h4>✓ BNC Bank Found and Available</h4>\n";
echo "<p><strong>Code:</strong> " . htmlspecialchars($bank['Code'] ?? '') . "</p>\n";
echo "<p><strong>Name:</strong> " . htmlspecialchars($bank['Name'] ?? '') . "</p>\n";
echo "<p><strong>Services:</strong> " . htmlspecialchars($bank['Services'] ?? '') . "</p>\n";
echo "</div>\n";
break;
}
}
}
public function __destruct() {
echo "</body>\n</html>";
}
}
// Check if required extensions are available
if (!extension_loaded('openssl')) {
die("<p class='error'><strong>ERROR:</strong> OpenSSL extension is required but not loaded.</p>\n");
}
if (!extension_loaded('mbstring')) {
die("<p class='error'><strong>ERROR:</strong> MBString extension is required for UTF-8 handling but not loaded.</p>\n");
}
if (!function_exists('hash_pbkdf2')) {
die("<p class='error'><strong>ERROR:</strong> hash_pbkdf2() function is required but not available. Please upgrade PHP.</p>\n");
}
// Run the test
$test = new PagoMovilBankTest();
$success = $test->runTests();
echo "<hr>\n";
if ($success) {
echo "<h2 class='success'>✓ TEST COMPLETED SUCCESSFULLY</h2>\n";
echo "<p>The bank list has been successfully retrieved and displayed in both table and dropdown format.</p>\n";
} else {
echo "<h2 class='error'>✗ TEST FAILED</h2>\n";
echo "<p>Check the error messages above for troubleshooting.</p>\n";
}
echo "<h3>Next Steps for Your WooCommerce Plugin:</h3>\n";
echo "<ul>\n";
echo "<li>Use the 'empty_object' format ({}) for bank list requests</li>\n";
echo "<li>Implement similar UTF-8 cleaning in your main plugin</li>\n";
echo "<li>Use the dropdown format: 'Code: Bank Name (Services)'</li>\n";
echo "<li>BNC bank code 0191 is confirmed available</li>\n";
echo "</ul>\n";
?>