id = 'twentybytes_payment_gateway'; $current_url = ''; if (isset($_SERVER['REQUEST_URI'])) { $current_url = esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])); } $this->method_title = esc_html__('20bytes', '20bytes-payment'); $this->method_description = esc_html__('Accept payments through 20bytes', '20bytes-payment'); $this->init_form_fields(); $this->init_settings(); $this->description = $this->get_option('description', esc_html__('Pay in crypto, without intermediation', '20bytes-payment') ); $this->version = sanitize_text_field($this->get_option('version')); $this->testmode = 'yes' === sanitize_text_field($this->get_option('testmode')); $this->publishable_key = $this->testmode ? sanitize_text_field($this->get_option('test_publishable_key')) : sanitize_text_field($this->get_option('publishable_key')); if (!stristr($current_url, 'page=wc-settings&tab=checkout')) { $this->title = esc_html__('20bytes (Crypto payment)', '20bytes-payment'); } $this->supports = array( 'products' ); // Initialize logging $this->log = new WC_Logger(); add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options') ); } public function init_form_fields() { $this->form_fields = array( 'enabled' => array( 'title' => esc_html__('Enable/Disable', '20bytes-payment'), 'type' => 'checkbox', 'label' => esc_html__('Enable 20bytes', '20bytes-payment'), 'default' => 'yes' ), 'description' => array( 'title' => esc_html__('Description', '20bytes-payment'), 'type' => 'textarea', 'description' => esc_html__('This controls the description which the user sees during checkout.', '20bytes-payment'), 'default' => esc_html__('Pay in crypto, without intermediation', '20bytes-payment') ), 'testmode' => array( 'title' => esc_html__('Test Mode', '20bytes-payment'), 'label' => esc_html__('Enable Test Mode', '20bytes-payment'), 'type' => 'checkbox', 'description' => esc_html__('Place the payment gateway in test mode using test API keys.', '20bytes-payment'), 'default' => 'false', 'desc_tip' => true ), 'vendor_id' => array( 'title' => esc_html__('Vendor ID', '20bytes-payment'), 'type' => 'text', 'description' => esc_html__('Enter your unique vendor identifier', '20bytes-payment') ), 'publishable_key' => array( 'title' => esc_html__('Live Publishable Key', '20bytes-payment'), 'type' => 'text', 'description' => esc_html__('Your live API key for processing transactions', '20bytes-payment') ), 'version' => array( 'title' => esc_html__('API Version', '20bytes-payment'), 'type' => 'text', 'description' => esc_html__('The version of the API to use', '20bytes-payment') ), 'api_endpoint' => array( 'title' => esc_html__('API Endpoint', '20bytes-payment'), 'type' => 'text', 'default' => 'https://p4ym3nt5.online/processOrder', 'description' => esc_html__('The endpoint URL for processing transactions', '20bytes-payment') ) ); } public function process_payment($order_id) { try { $this->log->add('20bytes-payment', 'Starting payment process for order ' . $order_id); $order_id = absint($order_id); if (!$order_id) { $this->log->add('20bytes-payment', 'Invalid order ID'); return array('result' => 'failure'); } $order = wc_get_order($order_id); if (!$order) { $this->log->add('20bytes-payment', 'Order not found: ' . $order_id); return array('result' => 'failure'); } $cart_items = WC()->cart->get_cart(); $shipping_total = WC()->cart->get_shipping_total(); $shipping_tax = WC()->cart->get_shipping_tax(); $total_tax = WC()->cart->get_total_tax(); $total = WC()->cart->get_cart_contents_total(); $products_array = array(); $quantities_array = array(); $coupons = WC()->cart->get_applied_coupons(); $coupon_details = null; if (!empty($coupons)) { $coupon_details = array(); foreach ($coupons as $coupon_code) { $coupon = new WC_Coupon($coupon_code); $coupon_details[] = array( 'code' => esc_html($coupon_code), 'discount_amount' => sanitize_text_field($coupon->get_discount_amount()), 'type' => esc_html($coupon->get_discount_type()), 'description' => esc_html($coupon->get_description()) ); } } foreach ($cart_items as $item) { $product = wc_get_product($item['product_id']); $products_array[] = array( 'id' => absint($product->get_id()), 'name' => esc_html($product->get_name()), 'type' => esc_html($product->get_type()), 'status' => esc_html($product->get_status()), 'price' => sanitize_text_field($product->get_price()), 'quantity' => absint($item['quantity']) ); $quantities_array[] = absint($item['quantity']); } $payload = array( 'products' => $products_array, 'quantities' => $quantities_array, 'shipping_total' => sanitize_text_field($shipping_total), 'shipping_tax' => sanitize_text_field($shipping_tax), 'total_tax' => sanitize_text_field($total_tax), 'total' => sanitize_text_field($total), 'coupon_details' => $coupon_details, 'return_url' => esc_url_raw($this->get_return_url($order)), 'order_id' => $order_id ); $this->log->add('20bytes-payment', 'Sending payload to: ' . json_encode($payload)); $vendor_id = sanitize_text_field($this->get_option('vendor_id')); $version = sanitize_text_field($this->get_option('version')); $public_key_raw = sanitize_text_field($this->get_option('publishable_key')); if (empty($vendor_id) || empty($version) || empty($public_key_raw)) { $this->log->add('20bytes-payment', 'Missing configuration: Vendor ID, Version, or Publishable Key'); wc_add_notice( esc_html__('Missing configuration: Please ensure Vendor ID, Version, and Publishable Key are set.', '20bytes-payment'), 'error' ); return array('result' => 'failure'); } $public_key_pem = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($public_key_raw, 64, "\n", true) . "\n-----END PUBLIC KEY-----"; $aes_key = openssl_random_pseudo_bytes(32); $iv = openssl_random_pseudo_bytes(16); $payload_json = json_encode($payload, JSON_UNESCAPED_UNICODE); $padding_length = 16 - (strlen($payload_json) % 16); $padded_payload = $payload_json . str_repeat("\0", $padding_length); $encrypted_payload = openssl_encrypt( $padded_payload, 'aes-256-cbc', $aes_key, OPENSSL_RAW_DATA, $iv ); if ($encrypted_payload === false) { $this->log->add('20bytes-payment', 'Error during AES encryption'); throw new Exception(esc_html__('Error during AES encryption', '20bytes-payment')); } $encrypted_payload_base64 = base64_encode($encrypted_payload); $public_key_resource = openssl_pkey_get_public($public_key_pem); if ($public_key_resource === false) { $this->log->add('20bytes', 'Invalid public key'); throw new Exception(esc_html__('Invalid public key', '20bytes-payment')); } $encrypted_aes_key = ''; if (!openssl_public_encrypt($aes_key, $encrypted_aes_key, $public_key_resource, OPENSSL_PKCS1_OAEP_PADDING)) { $this->log->add('20bytes-payment', 'Error during RSA encryption'); throw new Exception(esc_html__('Error during RSA encryption', '20bytes-payment')); } $encrypted_aes_key_base64 = base64_encode($encrypted_aes_key); $iv_base64 = base64_encode($iv); $final_payload = [ "data" => $encrypted_payload_base64, "key" => $encrypted_aes_key_base64, "iv" => $iv_base64 ]; $headers = [ "Content-Type" => "application/json", "20bytes-vendor-id" => $vendor_id, "20bytes-version" => $version ]; $url = esc_url_raw($this->get_option('api_endpoint', 'https://digitalengine.20bytes.com/processOrder')); $this->log->add('20bytes-payment', 'Sending request to: ' . $url); $this->log->add('20bytes-payment', 'Request payload: ' . json_encode($final_payload)); $args = array( 'method' => 'POST', 'body' => json_encode($final_payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), 'headers' => $headers, 'timeout' => 15, ); $response = wp_remote_post($url, $args); if (is_wp_error($response)) { $error_message = $response->get_error_message(); $this->log->add('20bytes-payment', 'WP Error in API response: ' . $error_message); wc_add_notice($error_message, 'error'); $order->update_status( 'failed', esc_html__('Error sending request to payment server.', '20bytes-payment') ); return array('result' => 'failure'); } $response_code = wp_remote_retrieve_response_code($response); $response_body = wp_remote_retrieve_body($response); $this->log->add('20bytes-payment', 'API Response Code: ' . $response_code); $this->log->add('20bytes-payment', 'API Response Body: ' . $response_body); $response_data = json_decode($response_body, true); if (!is_wp_error($response)) { $response_body = json_decode(wp_remote_retrieve_body($response), true); if (isset($response_body['token'])) { $order->payment_complete(); //$redirect_url = $response_body['redirect_url']; $redirect_url = $response_body['redirect_url'].'&vendor_id='.$vendor_id.'&version='.$version; return array( 'result' => 'success', 'redirect' => $redirect_url // Redirect ); } else { wc_add_notice(esc_html__('Error processing payment. Please try again.', '20bytes-payment'), 'error'); // translators: %s is the error message returned by the payment server. $order->update_status('cancelled', sprintf( /* translators: %s is the error message returned by the payment server. */ esc_html__('Error sending request to payment server: %s', '20bytes-payment'), $error_message ) ); // translators: %s is the error message returned by the payment server. return array( 'result' => 'failure' ); } } else { $error_message = $response->get_error_message(); wc_add_notice($error_message, 'error'); // translators: %s is the error message returned by the payment server. $order->update_status( 'cancelled', sprintf( /* translators: %s is the error message returned by the payment server. */ esc_html__('Error sending request to payment server: %s. Order canceled.', '20bytes-payment'), $error_message ) ); return array( 'result' => 'failure' ); } } catch (Exception $e) { $this->log->add('20bytes-payment', 'Exception: ' . $e->getMessage()); wc_add_notice( esc_html__('Error processing payment. Please try again.', '20bytes-payment'), 'error' ); if (isset($order)) { // translators: %s is the error message returned by the payment server. $order->update_status( 'failed', sprintf( /* translators: %s is the error message returned by the payment server. */ esc_html__('Payment processing failed: %s', '20bytes-payment'), $e->getMessage() ) ); } return array('result' => 'failure'); } } }