Merge pull request #40 from cryptochangements34/master

Allow Transaction Verification Without Monero-Wallet-RPC
This commit is contained in:
cryptochangements34 2018-01-23 16:52:19 -06:00 committed by GitHub
commit 6326652a19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 319 additions and 87 deletions

View File

@ -12,6 +12,7 @@ class Monero_Gateway extends WC_Payment_Gateway
private $discount; private $discount;
private $confirmed = false; private $confirmed = false;
private $monero_daemon; private $monero_daemon;
private $non_rpc = false;
function __construct() function __construct()
{ {
@ -30,7 +31,20 @@ class Monero_Gateway extends WC_Payment_Gateway
$this->host = $this->get_option('daemon_host'); $this->host = $this->get_option('daemon_host');
$this->port = $this->get_option('daemon_port'); $this->port = $this->get_option('daemon_port');
$this->address = $this->get_option('monero_address'); $this->address = $this->get_option('monero_address');
$this->viewKey = $this->get_option('viewKey');
$this->discount = $this->get_option('discount'); $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: // After init_settings() is called, you can get the settings and load them into variables, e.g:
// $this->title = $this->get_option('title' ); // $this->title = $this->get_option('title' );
@ -77,12 +91,32 @@ class Monero_Gateway extends WC_Payment_Gateway
'default' => __('Pay securely using XMR.', 'monero_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( 'monero_address' => array(
'title' => __('Monero Address', 'monero_gateway'), 'title' => __('Monero Address', 'monero_gateway'),
'label' => __('Useful for people that have not a daemon online'), 'label' => __('Useful for people that have not a daemon online'),
'type' => 'text', 'type' => 'text',
'desc_tip' => __('Monero Wallet Address', 'monero_gateway') '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( 'daemon_host' => array(
'title' => __('Monero wallet rpc Host/ IP', 'monero_gateway'), 'title' => __('Monero wallet rpc Host/ IP', 'monero_gateway'),
'type' => 'text', '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'), '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'), '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'), 'type' => __('number'),
'default' => '5%' 'default' => '5'
), ),
'environment' => array( 'environment' => array(
@ -140,17 +174,19 @@ class Monero_Gateway extends WC_Payment_Gateway
public function admin_options() public function admin_options()
{ {
$this->log->add('Monero_gateway', '[SUCCESS] Monero Settings OK'); $this->log->add('Monero_gateway', '[SUCCESS] Monero Settings OK');
echo "<h1>Monero Payment Gateway</h1>"; echo "<h1>Monero Payment Gateway</h1>";
echo "<p>Welcome to Monero Extension for WooCommerce. Getting started: Make a connection with daemon <a href='https://reddit.com/u/serhack'>Contact Me</a>"; echo "<p>Welcome to Monero Extension for WooCommerce. Getting started: Make a connection with daemon <a href='https://reddit.com/u/serhack'>Contact Me</a>";
echo "<div style='border:1px solid #DDD;padding:5px 10px;font-weight:bold;color:#223079;background-color:#9ddff3;'>"; echo "<div style='border:1px solid #DDD;padding:5px 10px;font-weight:bold;color:#223079;background-color:#9ddff3;'>";
$this->getamountinfo();
if(!$this->non_rpc) // only try to get balance data if using wallet-rpc
$this->getamountinfo();
echo "</div>"; echo "</div>";
echo "<table class='form-table'>"; echo "<table class='form-table'>";
$this->generate_settings_html(); $this->generate_settings_html();
echo "</table>"; echo "</table>";
echo "<h4>Learn more about using monero-wallet-rpc <a href=\"https://github.com/monero-integrations/monerowp/blob/master/README.md\">here</a></h4>"; echo "<h4>Learn more about using monero-wallet-rpc <a href=\"https://github.com/monero-integrations/monerowp/blob/master/README.md\">here</a> and viewkeys <a href=\"https://getmonero.org/resources/moneropedia/viewkey.html\">here</a> </h4>";
} }
public function getamountinfo() public function getamountinfo()
@ -197,7 +233,15 @@ class Monero_Gateway extends WC_Payment_Gateway
public function validate_fields() public function validate_fields()
{ {
if ($this->check_monero() != TRUE) { if ($this->check_monero() != TRUE) {
echo "<div class=\"error\"><p>Your Monero Address doesn't seem valid. Have you checked it?</p></div>"; echo "<div class=\"error\"><p>Your Monero Address doesn't look valid. Have you checked it?</p></div>";
}
if(!$this->check_viewKey())
{
echo "<div class=\"error\"><p>Your ViewKey doesn't look valid. Have you checked it?</p></div>";
}
if($this->check_checkedBoxes())
{
echo "<div class=\"error\"><p>You must choose to either use monero-wallet-rpc or a ViewKey, not both</p></div>";
} }
} }
@ -213,6 +257,29 @@ class Monero_Gateway extends WC_Payment_Gateway
} }
return false; 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) public function is_virtual_in_cart($order_id)
{ {
@ -231,93 +298,164 @@ class Monero_Gateway extends WC_Payment_Gateway
public function instruction($order_id) public function instruction($order_id)
{ {
$order = wc_get_order($order_id); if($this->non_rpc)
$amount = floatval(preg_replace('#[^\d.]#', '', $order->get_total())); {
$payment_id = $this->set_paymentid_cookie(); $order = wc_get_order($order_id);
$currency = $order->get_currency(); $amount = floatval(preg_replace('#[^\d.]#', '', $order->get_total()));
$amount_xmr2 = $this->changeto($amount, $currency, $payment_id); $payment_id = $this->set_paymentid_cookie(32);
$address = $this->address; $currency = $order->get_currency();
if (!isset($address)) { $amount_xmr2 = $this->changeto($amount, $currency, $payment_id);
// If there isn't address (merchant missed that field!), $address will be the Monero address for donating :) $address = $this->address;
$address = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; 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 "<h4> We are waiting for your transaction to be confirmed </h4>";
}
echo "
<head>
<!--Import Google Icon Font-->
<link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet'>
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,800' rel='stylesheet'>
<link href='http://cdn.monerointegrations.com/style.css' rel='stylesheet'>
<!--Let browser know website is optimized for mobile-->
<meta name='viewport' content='width=device-width, initial-scale=1.0'/>
</head>
<body>
<!-- page container -->
<div class='page-container'>
<!-- monero container payment box -->
<div class='container-xmr-payment'>
<!-- header -->
<div class='header-xmr-payment'>
<span class='logo-xmr'><img src='http://cdn.monerointegrations.com/logomonero.png' /></span>
<span class='xmr-payment-text-header'><h2>MONERO PAYMENT</h2></span>
</div>
<!-- end header -->
<!-- xmr content box -->
<div class='content-xmr-payment'>
<div class='xmr-amount-send'>
<span class='xmr-label'>Send:</span>
<div class='xmr-amount-box'>".$amount_xmr2."</div>
<span class='xmr-label'>Payment ID:</span>
<div class='xmr-integrated-address-box'>".$payment_id."</div>
</div>
<div class='xmr-address'>
<span class='xmr-label'>To this address:</span>
<div class='xmr-address-box'>".$address."</div>
</div>
<div class='xmr-qr-code'>
<span class='xmr-label'>Or scan QR:</span>
<div class='xmr-qr-code-box'><img src='https://api.qrserver.com/v1/create-qr-code/? size=200x200&data=".$uri."' /></div>
</div>
<div class='clear'></div>
</div>
<!-- end content box -->
<!-- footer xmr payment -->
<div class='footer-xmr-payment'>
<a href='https://getmonero.org' target='_blank'>Help</a> | <a href='https://getmonero.org' target='_blank'>About Monero</a>
</div>
<!-- end footer xmr payment -->
</div>
<!-- end monero container payment box -->
</div>
<!-- end page container -->
</body>
";
echo "
<script type='text/javascript'>setTimeout(function () { location.reload(true); }, $this->reloadTime);</script>";
} }
$uri = "monero:$address?amount=$amount?payment_id=$payment_id"; else
$array_integrated_address = $this->monero_daemon->make_integrated_address($payment_id); {
if (!isset($array_integrated_address)) { $order = wc_get_order($order_id);
$this->log->add('Monero_Gateway', '[ERROR] Unable get integrated address'); $amount = floatval(preg_replace('#[^\d.]#', '', $order->get_total()));
// Seems that we can't connect with daemon, then set array_integrated_address, little hack $payment_id = $this->set_paymentid_cookie(8);
$array_integrated_address["integrated_address"] = $address; $currency = $order->get_currency();
} $amount_xmr2 = $this->changeto($amount, $currency, $payment_id);
if($this->is_virtual_in_cart($order_id) == true){ $address = $this->address;
echo "test"; if (!isset($address)) {
} // If there isn't address (merchant missed that field!), $address will be the Monero address for donating :)
$message = $this->verify_payment($payment_id, $amount_xmr2, $order); $address = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A";
if ($this->confirmed) { }
$color = "006400"; $uri = "monero:$address?amount=$amount?payment_id=$payment_id";
} else { $array_integrated_address = $this->monero_daemon->make_integrated_address($payment_id);
$color = "DC143C"; if (!isset($array_integrated_address)) {
} $this->log->add('Monero_Gateway', '[ERROR] Unable get integrated address');
echo "<h4><font color=$color>" . $message . "</font></h4>"; // 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 "<h4><font color=$color>" . $message . "</font></h4>";
echo " echo "
<head> <head>
<!--Import Google Icon Font--> <!--Import Google Icon Font-->
<link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet'> <link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet'>
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,800' rel='stylesheet'> <link href='https://fonts.googleapis.com/css?family=Montserrat:400,800' rel='stylesheet'>
<link href='http://cdn.monerointegrations.com/style.css' rel='stylesheet'> <link href='http://127.0.0.1:8888/style.css' rel='stylesheet'>
<!--Let browser know website is optimized for mobile--> <!--Let browser know website is optimized for mobile-->
<meta name='viewport' content='width=device-width, initial-scale=1.0'/> <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
</head> </head>
<body> <body>
<!-- page container --> <!-- page container -->
<div class='page-container'> <div class='page-container'>
<!-- monero container payment box --> <!-- monero container payment box -->
<div class='container-xmr-payment'> <div class='container-xmr-payment'>
<!-- header --> <!-- header -->
<div class='header-xmr-payment'> <div class='header-xmr-payment'>
<span class='logo-xmr'><img src='http://cdn.monerointegrations.com/logomonero.png' /></span> <span class='logo-xmr'><img src='http://cdn.monerointegrations.com/logomonero.png' /></span>
<span class='xmr-payment-text-header'><h2>MONERO PAYMENT</h2></span> <span class='xmr-payment-text-header'><h2>MONERO PAYMENT</h2></span>
</div> </div>
<!-- end header --> <!-- end header -->
<!-- xmr content box --> <!-- xmr content box -->
<div class='content-xmr-payment'> <div class='content-xmr-payment'>
<div class='xmr-amount-send'> <div class='xmr-amount-send'>
<span class='xmr-label'>Send:</span> <span class='xmr-label'>Send:</span>
<div class='xmr-amount-box'>".$amount_xmr2."</div><div class='xmr-box'>XMR</div> <div class='xmr-amount-box'>".$amount_xmr2."</div>
</div> </div>
<div class='xmr-address'> <div class='xmr-address'>
<span class='xmr-label'>To this address:</span> <span class='xmr-label'>To this address:</span>
<div class='xmr-address-box'>".$array_integrated_address['integrated_address']."</div> <div class='xmr-address-box'>".$array_integrated_address['integrated_address']."</div>
</div> </div>
<div class='xmr-qr-code'> <div class='xmr-qr-code'>
<span class='xmr-label'>Or scan QR:</span> <span class='xmr-label'>Or scan QR:</span>
<div class='xmr-qr-code-box'><img src='https://api.qrserver.com/v1/create-qr-code/? size=200x200&data=".$uri."' /></div> <div class='xmr-qr-code-box'><img src='https://api.qrserver.com/v1/create-qr-code/? size=200x200&data=".$uri."' /></div>
</div> </div>
<div class='clear'></div> <div class='clear'></div>
</div> </div>
<!-- end content box --> <!-- end content box -->
<!-- footer xmr payment --> <!-- footer xmr payment -->
<div class='footer-xmr-payment'> <div class='footer-xmr-payment'>
<a href='https://getmonero.org' target='_blank'>Help</a> | <a href='https://getmonero.org' target='_blank'>About Monero</a> <a href='https://getmonero.org' target='_blank'>Help</a> | <a href='https://getmonero.org' target='_blank'>About Monero</a>
</div> </div>
<!-- end footer xmr payment --> <!-- end footer xmr payment -->
</div> </div>
<!-- end monero container payment box --> <!-- end monero container payment box -->
</div> </div>
<!-- end page container --> <!-- end page container -->
</body> </body>
"; ";
echo "
<script type='text/javascript'>setTimeout(function () { location.reload(true); }, $this->reloadTime);</script>";
echo " }
<script type='text/javascript'>setTimeout(function () { location.reload(true); }, $this->reloadTime);</script>";
} }
private function set_paymentid_cookie() private function set_paymentid_cookie($size)
{ {
if (!isset($_COOKIE['payment_id'])) { 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); setcookie('payment_id', $payment_id, time() + 2700);
} }
else{ else{
@ -456,6 +594,41 @@ class Monero_Gateway extends WC_Payment_Gateway
} }
return $message; 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() public function do_ssl_check()
{ {

View File

@ -319,3 +319,62 @@ class Monero_Library
return $get_bulk_payments; 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++;
}
}
}