Changeset 3462628
- Timestamp:
- 02/16/2026 03:15:20 PM (6 weeks ago)
- Location:
- cryptocloud-crypto-payment-gateway/trunk
- Files:
-
- 2 edited
-
readme.txt (modified) (2 diffs)
-
woocryptocloud.php (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
cryptocloud-crypto-payment-gateway/trunk/readme.txt
r2982657 r3462628 1 1 === CryptoCloud - Crypto Payment Gateway === 2 2 Contributors: cryptocloud 3 Tags:cryptocurrency, crypto, bitcoin, donation, payment gateway, crypto payment gateway3 Tags:cryptocurrency, bitcoin payment gateway, cryptocurrency payments, payment gateway, crypto payment gateway 4 4 Requires at least:4.5 5 Tested up to:6. 3.25 Tested up to:6.9.1 6 6 Requires PHP: 5.6 7 7 License: GPL2 8 Stable tag: 2.1.28 Stable tag:3.0.0 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 10 11 CryptoCloud - cryptocurrency payment system for business. We offer to you a possibility to accept payments worldwide in the most popular cryptocurrencies USDT/ETH/BTC/LTC and many others.11 CryptoCloud - cryptocurrency payment system for business. We offer to you a possibility to accept payments worldwide in 40 cryptocurrencies. 12 12 13 13 == Description == … … 46 46 **Start accepting cryptocurrency right now!** 47 47 48 - [Demo payment page](https://demopay.cryptocloud.plus/? currency=USDT&network=TRC20&lang=en?utm_source=wordpress.org&utm_medium=module-shop&utm_campaign=woocommerce)48 - [Demo payment page](https://demopay.cryptocloud.plus/?lang=en?utm_source=wordpress.org&utm_medium=module-shop&utm_campaign=woocommerce) 49 49 - To learn about other features, please visit our [website](https://cryptocloud.plus/en?utm_source=wordpress.org&utm_medium=module-shop&utm_campaign=woocommerce) and [sign up](https://app.cryptocloud.plus/registration?lang=en?utm_source=wordpress.org&utm_medium=module-shop&utm_campaign=woocommerce) 50 50 - Any questions left? Contact our [support team](https://t.me/cryptocloud_supbot) -
cryptocloud-crypto-payment-gateway/trunk/woocryptocloud.php
r2982654 r3462628 1 1 <?php 2 3 2 /* 4 3 * Plugin Name: WooCommerce - Платежный модуль CryptoCloud … … 7 6 * Author URI: https://cryptocloud.plus/ 8 7 * Author: CryptoCloud 9 * Version: 2. 1.28 * Version: 2.3.2 10 9 * Text Domain: cryptocloud 11 10 * Domain Path: /languages … … 14 13 define('WCCC_Payment_Gateway', dirname(__FILE__)); 15 14 15 // Declare HPOS compatibility 16 add_action('before_woocommerce_init', function () { 17 if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { 18 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); 19 } 20 }); 21 16 22 add_action('plugins_loaded', 'init_woo_cryptocloud', 0); 17 23 18 function init_woo_cryptocloud() { 24 function init_woo_cryptocloud() 25 { 19 26 20 27 if (!class_exists('WC_Payment_Gateway')) return; 21 28 22 class WC_Gateway_CryptoCloud extends WC_Payment_Gateway { 23 24 function __construct() { 29 class WC_Gateway_CryptoCloud extends WC_Payment_Gateway 30 { 31 32 function __construct() 33 { 25 34 $this->id = 'cryptocloud'; 26 35 $this->method_title = "CryptoCloud Payment System"; 27 $this->method_description = "Adds the ability to accept payments via the CryptoCloud payment system to WooCommerce";36 $this->method_description = __("Adds the ability to accept payments via the CryptoCloud payment system to WooCommerce", 'cryptocloud'); 28 37 29 38 $this->init_form_fields(); … … 33 42 $this->description = $this->get_option('description'); 34 43 35 if (version_compare(WOOCOMMERCE_VERSION, '2.0.0', '>=') ) {44 if (version_compare(WOOCOMMERCE_VERSION, '2.0.0', '>=')) { 36 45 add_action('woocommerce_update_options_payment_gateways_' . $this->id, array(&$this, 'process_admin_options')); 37 46 } else { … … 42 51 } 43 52 44 function init_form_fields() { 53 function init_form_fields() 54 { 45 55 $this->form_fields = array( 46 56 'enabled' => array( … … 51 61 'title' => array( 52 62 'title' => "Name", 53 'type' => 'text ',54 'description' => "The name that the user sees during the payment",63 'type' => 'textarea', 64 'description' => __("The name that the user sees during the payment", 'cryptocloud'), 55 65 'default' => "CryptoCloud Payment System", 56 66 'desc_tip' => true, 67 'css' => 'height: 60px; width: 400px;', 57 68 ), 58 69 'description' => array( 59 70 'title' => "Description", 71 'type' => 'textarea', 72 'description' => __("The description that the user sees during the payment", 'cryptocloud'), 73 'default' => __("Accepting payment via cryptocurrency. It is possible to pay the bill using a bank card.", 'cryptocloud'), 74 'desc_tip' => true, 75 'css' => 'height: 80px; width: 400px;', 76 ), 77 'apikey' => array( 78 'title' => __('API KEY', 'cryptocloud'), 79 'type' => 'textarea', 80 'description' => __('API key from your CryptoCloud personal account.', 'cryptocloud'), 81 'css' => 'height: 60px; width: 400px;', 82 ), 83 'merchant_id' => array( 84 'title' => __('Shop ID', 'cryptocloud'), 60 85 'type' => 'text', 61 'description' => "The description that the user sees during the payment", 62 'default' => "Accepting payment via cryptocurrency. It is possible to pay the bill using a bank card.", 63 'desc_tip' => true, 64 ), 65 'apikey' => array( 66 'title' => 'API KEY', 67 'type' => 'text', 68 ), 69 'merchant_id' => array( 70 'title' => __('SHOP ID','cryptocloud'), 71 'type' => 'text', 86 'description' => __('Store ID from your CryptoCloud personal account.', 'cryptocloud'), 87 ), 88 'webhook_secret' => array( 89 'title' => __('Webhook Secret Key', 'cryptocloud'), 90 'type' => 'textarea', 91 'description' => __('A secret key for verifying the JWT signature of webhook notifications. You can find it in your project settings in CryptoCloud.', 'cryptocloud'), 92 'css' => 'height: 60px; width: 400px;', 72 93 ), 73 94 ); 74 95 } 75 76 function process_payment($order_id) { 96 97 function process_payment($order_id) 98 { 77 99 global $woocommerce; 78 100 … … 83 105 $amount = number_format($amount, 2, '.', ''); 84 106 85 $email =$order->get_billing_email();86 87 $ merchant_id = $this->get_option("merchant_id");107 $email = $order->get_billing_email(); 108 109 $shop_id = $this->get_option("merchant_id"); 88 110 $apikey = $this->get_option("apikey"); 89 111 90 112 $currency = get_woocommerce_currency(); 113 91 114 $data_request = array( 92 'shop_id' => $merchant_id,93 'amount' => $amount,94 'currency' => $currency,95 'order_id' =>$order_id,96 'email' 115 'shop_id' => $shop_id, 116 'amount' => $amount, 117 'currency' => $currency, 118 'order_id' => (string)$order_id, 119 'email' => $email 97 120 ); 98 121 99 122 $headers = array( 100 'Authorization'=> "Token " . $apikey 123 'Authorization' => "Token " . $apikey, 124 'Content-Type' => 'application/json' 101 125 ); 102 126 103 127 $args = array( 104 'body' => $data_request,105 'timeout' => '60',106 'httpversion' => '1. 0',107 'headers' => $headers,128 'body' => json_encode($data_request), 129 'timeout' => 30, 130 'httpversion' => '1.1', 131 'headers' => $headers, 108 132 ); 109 $response = wp_remote_post('https://api.cryptocloud.plus/v1/invoice/create', $args); 110 $json_data = wp_remote_retrieve_body($response); 111 $json_data = json_decode($json_data, true); 112 var_dump($json_data); 113 $url = $json_data['pay_url']; 114 $status= $json_data['status']; 115 if($status=='success'){ 116 $woocommerce->cart->empty_cart(); 117 } 118 119 return array('result' => $status, 'redirect' => $url); 120 } 121 122 public function callback() { 123 if (!isset($_POST['order_id'])) exit; 124 125 $order_id = intval($_POST['order_id']); 133 134 $response = wp_remote_post('https://api.cryptocloud.plus/v2/invoice/create', $args); 135 136 // Обработка ошибок HTTP 137 if (is_wp_error($response)) { 138 wc_add_notice('Connection to the payment system failed. Please try again later.', 'error'); 139 $this->log_error('HTTP Error: ' . $response->get_error_message()); 140 return; 141 } 142 143 $response_code = wp_remote_retrieve_response_code($response); 144 if ($response_code !== 200) { 145 wc_add_notice('The payment system is temporarily unavailable. Error code:' . $response_code, 'error'); 146 $this->log_error('HTTP Response Code: ' . $response_code . ', Body: ' . wp_remote_retrieve_body($response)); 147 return; 148 } 149 150 $json_data = json_decode(wp_remote_retrieve_body($response), true); 151 152 // Проверка структуры ответа 153 if (!$json_data || !isset($json_data['status']) || $json_data['status'] !== 'success') { 154 $error_msg = isset($json_data['message']) ? $json_data['message'] : 'Неизвестная ошибка'; 155 wc_add_notice('Error creating payment. ' . $error_msg, 'error'); 156 $this->log_error('Invalid API response: ' . wp_remote_retrieve_body($response)); 157 return; 158 } 159 160 if (!isset($json_data['result']['link'])) { 161 wc_add_notice('Error creating payment link.', 'error'); 162 $this->log_error('Missing link in response: ' . wp_remote_retrieve_body($response)); 163 return; 164 } 165 166 $url = $json_data['result']['link']; 167 $invoice_id = $json_data['result']['uuid']; 168 169 $order = wc_get_order($order_id); 170 $order->update_meta_data('_cryptocloud_invoice_id', $invoice_id); 171 $order->save(); 172 173 // Очищаем корзину только после успешного создания инвойса 174 $woocommerce->cart->empty_cart(); 175 176 return array( 177 'result' => 'success', 178 'redirect' => $url 179 ); 180 } 181 182 public function callback() 183 { 184 // Получаем сырое тело запроса 185 $raw_body = file_get_contents('php://input'); 186 $received_data = array(); 187 188 // Определяем тип контента и парсим данные 189 $content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : ''; 190 191 if (strpos($content_type, 'application/json') !== false) { 192 // JSON данные 193 $received_data = json_decode($raw_body, true); 194 if (json_last_error() !== JSON_ERROR_NONE) { 195 $this->log_error('Invalid JSON in webhook'); 196 status_header(400); 197 echo 'Invalid JSON'; 198 exit; 199 } 200 } else { 201 // Form-urlencoded данные 202 parse_str($raw_body, $received_data); 203 } 204 205 if (empty($received_data)) { 206 $this->log_error('Empty received data'); 207 status_header(400); 208 echo 'Empty data'; 209 exit; 210 } 211 212 // Проверяем обязательные поля 213 if (!isset($received_data['invoice_id']) || !isset($received_data['status'])) { 214 $this->log_error('Missing required fields in webhook'); 215 status_header(400); 216 echo 'Missing required fields'; 217 exit; 218 } 219 220 $invoice_id = sanitize_text_field($received_data['invoice_id']); 221 $status = sanitize_text_field($received_data['status']); 222 $order_id = isset($received_data['order_id']) ? intval($received_data['order_id']) : 0; 223 224 // Логируем основные данные callback 225 $this->log_info("Webhook received - Invoice: $invoice_id, Status: $status, Order: $order_id"); 226 227 // Проверяем JWT подпись если секретный ключ установлен 228 $webhook_secret = $this->get_option('webhook_secret'); 229 if (!empty($webhook_secret)) { 230 if (isset($received_data['token'])) { 231 $token = sanitize_text_field($received_data['token']); 232 if (!$this->verify_jwt_token($token, $invoice_id, $webhook_secret)) { 233 $this->log_error('JWT token verification failed for invoice: ' . $invoice_id); 234 status_header(401); 235 echo 'Invalid token'; 236 exit; 237 } 238 } else { 239 $this->log_error('No token parameter found in webhook data'); 240 status_header(400); 241 echo 'Missing token'; 242 exit; 243 } 244 } 245 246 // Находим заказ по ID из webhook или ищем по ID инвойса 247 if ($order_id === 0) { 248 $order_id = $this->find_order_by_invoice_id($invoice_id); 249 if ($order_id === 0) { 250 $this->log_error('Order not found for invoice: ' . $invoice_id); 251 status_header(404); 252 echo 'Order not found'; 253 exit; 254 } 255 } 126 256 127 257 $order = new WC_Order($order_id); 128 258 129 $amount = $order->get_total(); 130 $amount = str_replace(',', '.', $amount); 131 $amount = number_format($amount, 2, '.', ''); 132 133 $order->payment_complete(); 134 259 $order = wc_get_order($order_id); 260 $stored_invoice_id = $order->get_meta('_cryptocloud_invoice_id', true); 261 262 // Нормализуем идентификаторы для сравнения (убираем префикс "INV-" если есть) 263 $normalized_stored = str_replace('INV-', '', $stored_invoice_id); 264 $normalized_received = str_replace('INV-', '', $invoice_id); 265 266 if ($normalized_stored !== $normalized_received) { 267 $this->log_error('Invoice ID mismatch for order: ' . $order_id); 268 status_header(400); 269 echo 'Invoice ID mismatch'; 270 exit; 271 } 272 273 // Обрабатываем статусы согласно документации 274 switch ($status) { 275 case 'success': 276 case 'paid': 277 if (!$order->is_paid()) { 278 $order->payment_complete(); 279 $order->add_order_note('Payment successfully completed via CryptoCloud. Invoice ID: ' . $invoice_id); 280 $this->log_info('Payment completed for order: ' . $order_id); 281 } 282 break; 283 284 case 'failed': 285 if (!$order->has_status('failed')) { 286 $order->update_status('failed', 'Payment failed via CryptoCloud. Invoice ID: ' . $invoice_id); 287 $this->log_info('Payment failed for order: ' . $order_id); 288 } 289 break; 290 291 case 'created': 292 // Инвойс создан, но еще не оплачен - ничего не делаем 293 break; 294 295 default: 296 $this->log_error('Unknown invoice status: ' . $status); 297 status_header(400); 298 echo 'Unknown status'; 299 exit; 300 } 301 302 status_header(200); 303 echo 'OK'; 135 304 exit; 305 } 306 307 /** 308 * Проверяет JWT токен из параметра token 309 */ 310 private function verify_jwt_token($jwt_token, $expected_invoice_id, $secret_key) 311 { 312 // Разбираем JWT токен 313 $jwt_parts = explode('.', $jwt_token); 314 if (count($jwt_parts) !== 3) { 315 $this->log_error('Invalid JWT format'); 316 return false; 317 } 318 319 list($header, $payload, $signature) = $jwt_parts; 320 321 // Декодируем header и payload 322 $header_decoded = base64_decode(str_replace(['-', '_'], ['+', '/'], $header)); 323 $payload_decoded = base64_decode(str_replace(['-', '_'], ['+', '/'], $payload)); 324 325 $header_data = json_decode($header_decoded, true); 326 $payload_data = json_decode($payload_decoded, true); 327 328 // Проверяем алгоритм 329 if (!isset($header_data['alg']) || $header_data['alg'] !== 'HS256') { 330 $this->log_error('Invalid JWT algorithm'); 331 return false; 332 } 333 334 // Проверяем срок действия токена 335 if (isset($payload_data['exp']) && $payload_data['exp'] < time()) { 336 $this->log_error('JWT token expired'); 337 return false; 338 } 339 340 // Проверяем, что invoice_id в токене совпадает с ожидаемым 341 if (!isset($payload_data['id']) || $payload_data['id'] !== $expected_invoice_id) { 342 $this->log_error('JWT token invoice_id mismatch'); 343 return false; 344 } 345 346 // Проверяем подпись с помощью HMAC SHA256 347 $expected_signature = hash_hmac('sha256', $header . '.' . $payload, $secret_key, true); 348 $expected_signature_base64 = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($expected_signature)); 349 350 if (!hash_equals($expected_signature_base64, $signature)) { 351 $this->log_error('JWT signature mismatch'); 352 return false; 353 } 354 355 return true; 356 } 357 358 /** 359 * Ищет заказ по ID инвойса (HPOS compatible) 360 */ 361 private function find_order_by_invoice_id($invoice_id) 362 { 363 // Нормализуем идентификатор 364 $normalized_invoice_id = str_replace('INV-', '', $invoice_id); 365 366 // Используем WC_Order_Query для поиска по метаданным 367 $orders = wc_get_orders(array( 368 'limit' => 1, 369 'meta_query' => array( 370 array( 371 'key' => '_cryptocloud_invoice_id', 372 'value' => array($invoice_id, 'INV-' . $normalized_invoice_id), 373 'compare' => 'IN' 374 ) 375 ), 376 'return' => 'ids' 377 )); 378 379 return !empty($orders) ? $orders[0] : 0; 380 } 381 382 /** 383 * Логирование ошибок 384 */ 385 private function log_error($message) 386 { 387 if (function_exists('wc_get_logger')) { 388 $logger = wc_get_logger(); 389 $logger->error('CryptoCloud ERROR: ' . $message, array('source' => 'cryptocloud')); 390 } 391 } 392 393 /** 394 * Логирование информации 395 */ 396 private function log_info($message) 397 { 398 if (function_exists('wc_get_logger')) { 399 $logger = wc_get_logger(); 400 $logger->info('CryptoCloud INFO: ' . $message, array('source' => 'cryptocloud')); 401 } 136 402 } 137 403 } 138 404 } 139 405 140 function add_woo_cryptocloud($methods) { 141 $methods[] = 'WC_Gateway_CryptoCloud'; 406 function add_woo_cryptocloud($methods) 407 { 408 $methods[] = 'WC_Gateway_CryptoCloud'; 142 409 return $methods; 143 410 }
Note: See TracChangeset
for help on using the changeset viewer.