Handler <?php // Подключаем инициализационный файл WHMCS require_once __DIR__ . '/../../../init.php'; require_once __DIR__ . '/../../../includes/gatewayfunctions.php'; require_once __DIR__ . '/../../../includes/invoicefunctions.php'; // Название платежного шлюза (должно совпадать с именем файла шлюза без .php) $gatewayModuleName = basename(__FILE__, '.php'); // Получаем параметры шлюза $gatewayParams = getGatewayVariables($gatewayModuleName); // Проверяем, активен ли модуль if (!$gatewayParams['type']) { die("Module Not Activated"); } // Получаем токен из настроек шлюза $apiToken = $gatewayParams['token']; if (!$apiToken) { logTransaction($gatewayParams['name'], $_POST, "Error: API Token not configured"); die("API Token not configured"); } // Получаем заголовок с подписью $signatureHeader = isset($_SERVER['HTTP_CRYPTO_PAY_API_SIGNATURE']) ? $_SERVER['HTTP_CRYPTO_PAY_API_SIGNATURE'] : null; // Получаем тело запроса (raw POST data) $requestBody = file_get_contents('php://input'); // --- Проверка подписи --- if (!$signatureHeader) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody], "Error: Signature header missing"); http_response_code(400); // Bad Request die("Signature header missing"); } try { $secret = hash('sha256', $apiToken, true); // Используем бинарный хеш для секрета HMAC $calculatedHmac = hash_hmac('sha256', $requestBody, $secret); if (!hash_equals($calculatedHmac, $signatureHeader)) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody, 'received_signature' => $signatureHeader, 'calculated_hmac' => $calculatedHmac], "Error: Invalid Signature"); http_response_code(403); // Forbidden die("Invalid Signature"); } } catch (Exception $e) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody, 'error' => $e->getMessage()], "Error: Signature check failed"); http_response_code(500); // Internal Server Error die("Signature check failed"); } // --- /Проверка подписи --- // Декодируем JSON данные $updateData = json_decode($requestBody, true); if (json_last_error() !== JSON_ERROR_NONE) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody], "Error: Invalid JSON received"); http_response_code(400); die("Invalid JSON received"); } // Записываем полученные данные в лог для отладки logTransaction($gatewayParams['name'], $updateData, "Received Webhook"); // Проверяем тип обновления и наличие необходимых данных if (!isset($updateData['update_type']) || $updateData['update_type'] !== 'invoice_paid') { // Это не уведомление об оплате, игнорируем die("Not an invoice_paid update."); } if (!isset($updateData['payload']['payload']) || !isset($updateData['payload']['invoice_id']) || !isset($updateData['payload']['status'])) { logTransaction($gatewayParams['name'], $updateData, "Error: Missing required payload data (payload, invoice_id, status)"); http_response_code(400); die("Missing required payload data"); } // Извлекаем данные из payload вебхука $invoiceIdFromPayload = $updateData['payload']['payload']; // Наш ID счета из WHMCS $cryptoBotInvoiceId = $updateData['payload']['invoice_id']; // ID счета в Crypto Bot (для лога транзакций) $status = $updateData['payload']['status']; $currencyType = $updateData['payload']['currency_type'] ?? 'crypto'; // По умолчанию crypto, если не указано // Определяем сумму и валюту платежа $paymentAmount = null; $paymentFee = 0; // Комиссия $feeAsset = ''; $paidAsset = ''; if ($currencyType === 'fiat') { if (isset($updateData['payload']['paid_amount'])) { $paymentAmount = $updateData['payload']['paid_amount']; $paidAsset = $updateData['payload']['paid_asset'] ?? 'UNKNOWN'; } else { logTransaction($gatewayParams['name'], $updateData, "Error: Missing 'paid_amount' for fiat invoice"); http_response_code(400); die("Missing 'paid_amount' for fiat invoice"); } } elseif ($currencyType === 'crypto') { if (isset($updateData['payload']['amount'])) { $paymentAmount = $updateData['payload']['amount']; $paidAsset = $updateData['payload']['asset'] ?? 'UNKNOWN'; } else { logTransaction($gatewayParams['name'], $updateData, "Error: Missing 'amount' for crypto invoice"); http_response_code(400); die("Missing 'amount' for crypto invoice"); } } else { logTransaction($gatewayParams['name'], $updateData, "Error: Unknown currency_type"); http_response_code(400); die("Unknown currency_type"); } // Получаем комиссию, если она есть if (isset($updateData['payload']['fee_amount']) && isset($updateData['payload']['fee_asset'])) { $paymentFee = $updateData['payload']['fee_amount']; $feeAsset = $updateData['payload']['fee_asset']; } // Проверяем статус платежа if ($status !== 'paid') { logTransaction($gatewayParams['name'], $updateData, "Invoice status is not 'paid'. Status: " . $status); die("Invoice status is not 'paid'."); } // Проверяем счет в WHMCS (существует ли и не оплачен ли) $invoiceId = checkCbInvoiceID($invoiceIdFromPayload, $gatewayParams['name']); // Проверяем уникальность ID транзакции Crypto Bot checkCbTransID($cryptoBotInvoiceId); // Добавляем платеж в WHMCS if ($invoiceId && $paymentAmount > 0) { // Определяем валюту счета WHMCS для корректного добавления платежа // Обычно сумма в $paymentAmount уже должна быть в валюте счета, т.к. мы запрашивали счет в USD // Если бы мы принимали крипту напрямую, потребовалась бы конвертация $invoiceData = localAPI('GetInvoice', ['invoiceid' => $invoiceId]); $invoiceCurrency = getCurrency(null, $invoiceData['currencyid']); // Получаем детали валюты счета // Формируем сообщение для лога $logMessage = "Payment received in {$paidAsset}. CryptoBot Invoice ID: {$cryptoBotInvoiceId}."; if ($paymentFee > 0) { $logMessage .= " Fee: {$paymentFee} {$feeAsset}."; } addInvoicePayment( $invoiceId, $cryptoBotInvoiceId, // Используем ID счета Crypto Bot как уникальный ID транзакции $paymentAmount, // Сумма платежа (предполагаем, что она в валюте счета WHMCS) $paymentFee, // Комиссия шлюза (если есть) $gatewayModuleName ); logTransaction($gatewayParams['name'], $updateData, "Successful. " . $logMessage); echo "OK"; // Отвечаем Crypto Bot, что все успешно обработано } else { logTransaction($gatewayParams['name'], $updateData, "Error: Invoice ID validation failed or payment amount is zero."); http_response_code(400); // Можно использовать 404, если счет не найден die("Invoice ID validation failed or payment amount is zero."); } ?> PHP <?php // Подключаем инициализационный файл WHMCS require_once __DIR__ . '/../../../init.php'; require_once __DIR__ . '/../../../includes/gatewayfunctions.php'; require_once __DIR__ . '/../../../includes/invoicefunctions.php'; // Название платежного шлюза (должно совпадать с именем файла шлюза без .php) $gatewayModuleName = basename(__FILE__, '.php'); // Получаем параметры шлюза $gatewayParams = getGatewayVariables($gatewayModuleName); // Проверяем, активен ли модуль if (!$gatewayParams['type']) { die("Module Not Activated"); } // Получаем токен из настроек шлюза $apiToken = $gatewayParams['token']; if (!$apiToken) { logTransaction($gatewayParams['name'], $_POST, "Error: API Token not configured"); die("API Token not configured"); } // Получаем заголовок с подписью $signatureHeader = isset($_SERVER['HTTP_CRYPTO_PAY_API_SIGNATURE']) ? $_SERVER['HTTP_CRYPTO_PAY_API_SIGNATURE'] : null; // Получаем тело запроса (raw POST data) $requestBody = file_get_contents('php://input'); // --- Проверка подписи --- if (!$signatureHeader) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody], "Error: Signature header missing"); http_response_code(400); // Bad Request die("Signature header missing"); } try { $secret = hash('sha256', $apiToken, true); // Используем бинарный хеш для секрета HMAC $calculatedHmac = hash_hmac('sha256', $requestBody, $secret); if (!hash_equals($calculatedHmac, $signatureHeader)) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody, 'received_signature' => $signatureHeader, 'calculated_hmac' => $calculatedHmac], "Error: Invalid Signature"); http_response_code(403); // Forbidden die("Invalid Signature"); } } catch (Exception $e) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody, 'error' => $e->getMessage()], "Error: Signature check failed"); http_response_code(500); // Internal Server Error die("Signature check failed"); } // --- /Проверка подписи --- // Декодируем JSON данные $updateData = json_decode($requestBody, true); if (json_last_error() !== JSON_ERROR_NONE) { logTransaction($gatewayParams['name'], ['raw_body' => $requestBody], "Error: Invalid JSON received"); http_response_code(400); die("Invalid JSON received"); } // Записываем полученные данные в лог для отладки logTransaction($gatewayParams['name'], $updateData, "Received Webhook"); // Проверяем тип обновления и наличие необходимых данных if (!isset($updateData['update_type']) || $updateData['update_type'] !== 'invoice_paid') { // Это не уведомление об оплате, игнорируем die("Not an invoice_paid update."); } if (!isset($updateData['payload']['payload']) || !isset($updateData['payload']['invoice_id']) || !isset($updateData['payload']['status'])) { logTransaction($gatewayParams['name'], $updateData, "Error: Missing required payload data (payload, invoice_id, status)"); http_response_code(400); die("Missing required payload data"); } // Извлекаем данные из payload вебхука $invoiceIdFromPayload = $updateData['payload']['payload']; // Наш ID счета из WHMCS $cryptoBotInvoiceId = $updateData['payload']['invoice_id']; // ID счета в Crypto Bot (для лога транзакций) $status = $updateData['payload']['status']; $currencyType = $updateData['payload']['currency_type'] ?? 'crypto'; // По умолчанию crypto, если не указано // Определяем сумму и валюту платежа $paymentAmount = null; $paymentFee = 0; // Комиссия $feeAsset = ''; $paidAsset = ''; if ($currencyType === 'fiat') { if (isset($updateData['payload']['paid_amount'])) { $paymentAmount = $updateData['payload']['paid_amount']; $paidAsset = $updateData['payload']['paid_asset'] ?? 'UNKNOWN'; } else { logTransaction($gatewayParams['name'], $updateData, "Error: Missing 'paid_amount' for fiat invoice"); http_response_code(400); die("Missing 'paid_amount' for fiat invoice"); } } elseif ($currencyType === 'crypto') { if (isset($updateData['payload']['amount'])) { $paymentAmount = $updateData['payload']['amount']; $paidAsset = $updateData['payload']['asset'] ?? 'UNKNOWN'; } else { logTransaction($gatewayParams['name'], $updateData, "Error: Missing 'amount' for crypto invoice"); http_response_code(400); die("Missing 'amount' for crypto invoice"); } } else { logTransaction($gatewayParams['name'], $updateData, "Error: Unknown currency_type"); http_response_code(400); die("Unknown currency_type"); } // Получаем комиссию, если она есть if (isset($updateData['payload']['fee_amount']) && isset($updateData['payload']['fee_asset'])) { $paymentFee = $updateData['payload']['fee_amount']; $feeAsset = $updateData['payload']['fee_asset']; } // Проверяем статус платежа if ($status !== 'paid') { logTransaction($gatewayParams['name'], $updateData, "Invoice status is not 'paid'. Status: " . $status); die("Invoice status is not 'paid'."); } // Проверяем счет в WHMCS (существует ли и не оплачен ли) $invoiceId = checkCbInvoiceID($invoiceIdFromPayload, $gatewayParams['name']); // Проверяем уникальность ID транзакции Crypto Bot checkCbTransID($cryptoBotInvoiceId); // Добавляем платеж в WHMCS if ($invoiceId && $paymentAmount > 0) { // Определяем валюту счета WHMCS для корректного добавления платежа // Обычно сумма в $paymentAmount уже должна быть в валюте счета, т.к. мы запрашивали счет в USD // Если бы мы принимали крипту напрямую, потребовалась бы конвертация $invoiceData = localAPI('GetInvoice', ['invoiceid' => $invoiceId]); $invoiceCurrency = getCurrency(null, $invoiceData['currencyid']); // Получаем детали валюты счета // Формируем сообщение для лога $logMessage = "Payment received in {$paidAsset}. CryptoBot Invoice ID: {$cryptoBotInvoiceId}."; if ($paymentFee > 0) { $logMessage .= " Fee: {$paymentFee} {$feeAsset}."; } addInvoicePayment( $invoiceId, $cryptoBotInvoiceId, // Используем ID счета Crypto Bot как уникальный ID транзакции $paymentAmount, // Сумма платежа (предполагаем, что она в валюте счета WHMCS) $paymentFee, // Комиссия шлюза (если есть) $gatewayModuleName ); logTransaction($gatewayParams['name'], $updateData, "Successful. " . $logMessage); echo "OK"; // Отвечаем Crypto Bot, что все успешно обработано } else { logTransaction($gatewayParams['name'], $updateData, "Error: Invoice ID validation failed or payment amount is zero."); http_response_code(400); // Можно использовать 404, если счет не найден die("Invoice ID validation failed or payment amount is zero."); } ?> Initiator <?php function cryptobot_config() { $configarray = array( "FriendlyName" => array( "Type" => "System", "Value" => "cryptobot" ), "token" => array( "FriendlyName" => "Token", "Type" => "text", "Size" => "128", "Description" => "Enter the API interaction token" ) ); return $configarray; } function cryptobot_link($params){ $gatewayParams = getGatewayVariables('cryptobot'); if(!$gatewayParams['token']){ die('Not all data in the module settings are specified'); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://pay.crypt.bot/api/createInvoice?currency_type=fiat&fiat=USD&amount=".$params['amount']."&payload=".$params['invoiceid']."&paid_btn_name=callback&paid_btn_url=https://".$_SERVER['HTTP_HOST']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Crypto-Pay-API-Token: ".$gatewayParams["token"] ]); $result = json_decode(curl_exec($ch), true); if(curl_errno($ch)){ header("Location: /"); } if(!$result["ok"]){ header("Location: /"); } header("Location: ".$result["result"]["bot_invoice_url"]); } ?> PHP <?php function cryptobot_config() { $configarray = array( "FriendlyName" => array( "Type" => "System", "Value" => "cryptobot" ), "token" => array( "FriendlyName" => "Token", "Type" => "text", "Size" => "128", "Description" => "Enter the API interaction token" ) ); return $configarray; } function cryptobot_link($params){ $gatewayParams = getGatewayVariables('cryptobot'); if(!$gatewayParams['token']){ die('Not all data in the module settings are specified'); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://pay.crypt.bot/api/createInvoice?currency_type=fiat&fiat=USD&amount=".$params['amount']."&payload=".$params['invoiceid']."&paid_btn_name=callback&paid_btn_url=https://".$_SERVER['HTTP_HOST']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Crypto-Pay-API-Token: ".$gatewayParams["token"] ]); $result = json_decode(curl_exec($ch), true); if(curl_errno($ch)){ header("Location: /"); } if(!$result["ok"]){ header("Location: /"); } header("Location: ".$result["result"]["bot_invoice_url"]); } ?> Не забудь вкл и указать вебхук в CryptoBot - https://твойдомен/modules/gateways/callback/cryptobot.php Скачать - https://github.com/sad3qwdfsdf/whmcs-module-cryptobot VirusTotal - https://www.virustotal.com/gui/file/2379ec14dfd46b0355823c830641ca0d38e94e72cccf00408e9d8abf7d9e2a5d/detection Отказ от ответственности Предоставляется как есть Любые понесённый вами убытки не будут компенсированы Обновления не обещаю Помощи с установкой нет На момент 09.04.2025 работает