diff --git a/monero/include/monero_payments.php b/monero/include/monero_payments.php index 3ba2c22..cc4a55c 100644 --- a/monero/include/monero_payments.php +++ b/monero/include/monero_payments.php @@ -12,6 +12,7 @@ class Monero_Gateway extends WC_Payment_Gateway private $discount; private $confirmed = false; private $monero_daemon; + private $non_rpc = false; function __construct() { @@ -30,7 +31,20 @@ class Monero_Gateway extends WC_Payment_Gateway $this->host = $this->get_option('daemon_host'); $this->port = $this->get_option('daemon_port'); $this->address = $this->get_option('monero_address'); + $this->viewKey = $this->get_option('viewKey'); $this->discount = $this->get_option('discount'); + + $this->use_viewKey = $this->get_option('use_viewKey'); + $this->use_rpc = $this->get_option('use_rpc'); + + if($this->use_viewKey == 'yes') + { + $this->non_rpc = true; + } + if($this->use_rpc == 'yes') + { + $this->non_rpc = false; + } // After init_settings() is called, you can get the settings and load them into variables, e.g: // $this->title = $this->get_option('title' ); @@ -77,12 +91,32 @@ class Monero_Gateway extends WC_Payment_Gateway 'default' => __('Pay securely using XMR.', 'monero_gateway') ), + 'use_viewKey' => array( + 'title' => __('Use ViewKey', 'monero_gateway'), + 'label' => __(' Verify Transaction with ViewKey ', 'monero_gateway'), + 'type' => 'checkbox', + 'description' => __('Fill in the Address and ViewKey fields to verify transactions with your ViewKey', 'monero_gateway'), + 'default' => 'no' + ), 'monero_address' => array( 'title' => __('Monero Address', 'monero_gateway'), 'label' => __('Useful for people that have not a daemon online'), 'type' => 'text', 'desc_tip' => __('Monero Wallet Address', 'monero_gateway') ), + 'viewKey' => array( + 'title' => __('Secret ViewKey', 'monero_gateway'), + 'label' => __('Secret ViewKey'), + 'type' => 'text', + 'desc_tip' => __('Your secret ViewKey', 'monero_gateway') + ), + 'use_rpc' => array( + 'title' => __('Use monero-wallet-rpc', 'monero_gateway'), + 'label' => __(' Verify transactions with the monero-wallet-rpc ', 'monero_gateway'), + 'type' => 'checkbox', + 'description' => __('This must be setup seperatly', 'monero_gateway'), + 'default' => 'no' + ), 'daemon_host' => array( 'title' => __('Monero wallet rpc Host/ IP', 'monero_gateway'), 'type' => 'text', @@ -100,8 +134,8 @@ class Monero_Gateway extends WC_Payment_Gateway 'desc_tip' => __('Provide a discount to your customers for making a private payment with XMR!', 'monero_gateway'), 'description' => __('Do you want to spread the word about Monero? Offer a small discount! Leave this empty if you do not wish to provide a discount', 'monero_gateway'), - 'type' => __('text'), - 'default' => '5%' + 'type' => __('number'), + 'default' => '5' ), 'environment' => array( @@ -140,17 +174,19 @@ class Monero_Gateway extends WC_Payment_Gateway public function admin_options() { $this->log->add('Monero_gateway', '[SUCCESS] Monero Settings OK'); - echo "

Monero Payment Gateway

"; echo "

Welcome to Monero Extension for WooCommerce. Getting started: Make a connection with daemon Contact Me"; echo "

"; - $this->getamountinfo(); + + if(!$this->non_rpc) // only try to get balance data if using wallet-rpc + $this->getamountinfo(); + echo "
"; echo ""; $this->generate_settings_html(); echo "
"; - echo "

Learn more about using monero-wallet-rpc here

"; + echo "

Learn more about using monero-wallet-rpc here and viewkeys here

"; } public function getamountinfo() @@ -197,7 +233,15 @@ class Monero_Gateway extends WC_Payment_Gateway public function validate_fields() { if ($this->check_monero() != TRUE) { - echo "

Your Monero Address doesn't seem valid. Have you checked it?

"; + echo "

Your Monero Address doesn't look valid. Have you checked it?

"; + } + if(!$this->check_viewKey()) + { + echo "

Your ViewKey doesn't look valid. Have you checked it?

"; + } + if($this->check_checkedBoxes()) + { + echo "

You must choose to either use monero-wallet-rpc or a ViewKey, not both

"; } } @@ -213,6 +257,29 @@ class Monero_Gateway extends WC_Payment_Gateway } return false; } + public function check_viewKey() + { + if($this->use_viewKey == 'yes') + { + if (strlen($this->viewKey) == 64) { + return true; + } + return false; + } + return true; + } + public function check_checkedBoxes() + { + if($this->use_viewKey == 'yes') + { + if($this->use_rpc == 'yes') + { + return true; + } + } + else + return false; + } public function is_virtual_in_cart($order_id) { @@ -231,93 +298,164 @@ class Monero_Gateway extends WC_Payment_Gateway public function instruction($order_id) { - $order = wc_get_order($order_id); - $amount = floatval(preg_replace('#[^\d.]#', '', $order->get_total())); - $payment_id = $this->set_paymentid_cookie(); - $currency = $order->get_currency(); - $amount_xmr2 = $this->changeto($amount, $currency, $payment_id); - $address = $this->address; - if (!isset($address)) { - // If there isn't address (merchant missed that field!), $address will be the Monero address for donating :) - $address = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; + if($this->non_rpc) + { + $order = wc_get_order($order_id); + $amount = floatval(preg_replace('#[^\d.]#', '', $order->get_total())); + $payment_id = $this->set_paymentid_cookie(32); + $currency = $order->get_currency(); + $amount_xmr2 = $this->changeto($amount, $currency, $payment_id); + $address = $this->address; + if (!isset($address)) { + // If there isn't address (merchant missed that field!), $address will be the Monero address for donating :) + $address = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; + } + $uri = "monero:$address?amount=$amount?payment_id=$payment_id"; + + if($this->verify_non_rpc($payment_id, $amount_xmr2, $order_id) == false); + { + echo "

We are waiting for your transaction to be confirmed

"; + } + + echo " + + + + + + + + + + +
+ +
+ +
+ +

MONERO PAYMENT

+
+ + +
+
+ Send: +
".$amount_xmr2."
+ Payment ID: +
".$payment_id."
+
+
+ To this address: +
".$address."
+
+
+ Or scan QR: +
+
+
+
+ + + + +
+ +
+ + + "; + + echo " + "; } - $uri = "monero:$address?amount=$amount?payment_id=$payment_id"; - $array_integrated_address = $this->monero_daemon->make_integrated_address($payment_id); - if (!isset($array_integrated_address)) { - $this->log->add('Monero_Gateway', '[ERROR] Unable get integrated address'); - // Seems that we can't connect with daemon, then set array_integrated_address, little hack - $array_integrated_address["integrated_address"] = $address; - } - if($this->is_virtual_in_cart($order_id) == true){ - echo "test"; - } - $message = $this->verify_payment($payment_id, $amount_xmr2, $order); - if ($this->confirmed) { - $color = "006400"; - } else { - $color = "DC143C"; - } - echo "

" . $message . "

"; + else + { + $order = wc_get_order($order_id); + $amount = floatval(preg_replace('#[^\d.]#', '', $order->get_total())); + $payment_id = $this->set_paymentid_cookie(8); + $currency = $order->get_currency(); + $amount_xmr2 = $this->changeto($amount, $currency, $payment_id); + $address = $this->address; + if (!isset($address)) { + // If there isn't address (merchant missed that field!), $address will be the Monero address for donating :) + $address = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; + } + $uri = "monero:$address?amount=$amount?payment_id=$payment_id"; + $array_integrated_address = $this->monero_daemon->make_integrated_address($payment_id); + if (!isset($array_integrated_address)) { + $this->log->add('Monero_Gateway', '[ERROR] Unable get integrated address'); + // Seems that we can't connect with daemon, then set array_integrated_address, little hack + $array_integrated_address["integrated_address"] = $address; + } + $message = $this->verify_payment($payment_id, $amount_xmr2, $order); + if ($this->confirmed) { + $color = "006400"; + } else { + $color = "DC143C"; + } + echo "

" . $message . "

"; - echo " - - - - - - - - - - -
- -
- -
- -

MONERO PAYMENT

-
- - -
-
- Send: -
".$amount_xmr2."
XMR
-
-
- To this address: -
".$array_integrated_address['integrated_address']."
-
-
- Or scan QR: -
-
-
-
- - - - -
- -
- - - "; - - - - echo " - "; + echo " + + + + + + + + + + +
+ +
+ +
+ +

MONERO PAYMENT

+
+ + +
+
+ Send: +
".$amount_xmr2."
+
+
+ To this address: +
".$array_integrated_address['integrated_address']."
+
+
+ Or scan QR: +
+
+
+
+ + + + +
+ +
+ + + "; + + echo " + "; + } } - private function set_paymentid_cookie() + private function set_paymentid_cookie($size) { if (!isset($_COOKIE['payment_id'])) { - $payment_id = bin2hex(openssl_random_pseudo_bytes(8)); + $payment_id = bin2hex(openssl_random_pseudo_bytes($size)); setcookie('payment_id', $payment_id, time() + 2700); } else{ @@ -456,6 +594,41 @@ class Monero_Gateway extends WC_Payment_Gateway } return $message; } + + public function verify_non_rpc($payment_id, $amount, $order_id) + { + $tools = new NodeTools(); + $bc_height = $tools->get_last_block_height(); + $txs_from_block = $tools->get_txs_from_block($bc_height); + $tx_count = count($txs_from_block) - 1; // The tx at index 0 is a coinbase tx so it can be ignored + + $i = 1; + $output_found; + $block_index; + while($i <= $tx_count) + { + $tx_hash = $txs_from_block[$i]['tx_hash']; + $result = $tools->check_tx($tx_hash, $this->address, $this->viewKey); + if($result) + { + $output_found = $result; + $block_index = $i; + $i = $tx_count; // finish loop + } + $i++; + } + if(isset($output_found)) + { + $amount_atomic_units = $amount * 1000000000000; + if($txs_from_block[$block_index]['payment_id'] == $payment_id && $output_found >= $amount) + { + $this->on_verified($payment_id, $amount_atomic_units, $order_id); + } + + return true; + } + return false; + } public function do_ssl_check() { diff --git a/monero/library.php b/monero/library.php index 1e3d777..774a6a1 100644 --- a/monero/library.php +++ b/monero/library.php @@ -319,3 +319,62 @@ class Monero_Library return $get_bulk_payments; } } + +class NodeTools +{ + public function get_last_block_height() + { + $curl = curl_init(); + + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => 'https://xmrchain.net/api/networkinfo', + )); + $resp = curl_exec($curl); + curl_close($curl); + + $array = json_decode($resp, true); + return $array['data']['height'] - 1; + } + + public function get_txs_from_block($height) + { + $curl = curl_init(); + + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => 'https://xmrchain.net/api/search/' . $height, + )); + $resp = curl_exec($curl); + curl_close($curl); + + $array = json_decode($resp, true); + + return $array['data']['txs']; + } + + public function check_tx($tx_hash, $address, $viewKey) + { + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => 'https://xmrchain.net/api/outputs?txhash=' .$tx_hash . '&address='. $address . '&viewkey='. $viewKey .'&txprove=0', + )); + $resp = curl_exec($curl); + curl_close($curl); + $array = json_decode($resp, true); + $output_count = count($array['data']['outputs']); + $i = 0; + while($i < $output_count) + { + if($array['data']['outputs'][$i]['match']) + { + return $array['data']['outputs'][$i]; + } + + $i++; + } + + } + +}