Merge pull request #2 from cryptochangements34/master

Adds Integrated Address support and updates library
This commit is contained in:
serhack 2017-07-18 08:35:10 +02:00 committed by GitHub
commit bb5f975026
2 changed files with 304 additions and 123 deletions

View file

@ -3,7 +3,8 @@
class Monero_Gateway extends WC_Payment_Gateway class Monero_Gateway extends WC_Payment_Gateway
{ {
private $monero_daemon;
function __construct() function __construct()
{ {
@ -53,6 +54,7 @@ class Monero_Gateway extends WC_Payment_Gateway
'process_admin_options' 'process_admin_options'
)); ));
} }
$this->monero_daemon = new Monero_Library($this->host . ':' . $this->port . '/json_rpc');
} }
public function admin_options() public function admin_options()
@ -136,7 +138,8 @@ class Monero_Gateway extends WC_Payment_Gateway
{ {
$xmr_live_price = $this->retriveprice($currency); $xmr_live_price = $this->retriveprice($currency);
$new_amount = $amount / $xmr_live_price; $new_amount = $amount / $xmr_live_price;
return $new_amount; $rounded_amount = round($new_amount, 12); //the moneo wallet can't handle decimals smaller than 0.000000000001
return $rounded_amount;
} }
@ -178,8 +181,7 @@ class Monero_Gateway extends WC_Payment_Gateway
return true; return true;
} }
return false; return false;
} }
public function instruction($order_id) public function instruction($order_id)
{ {
@ -188,8 +190,10 @@ class Monero_Gateway extends WC_Payment_Gateway
$currency = $order->currency; $currency = $order->currency;
$amount_xmr2 = $this->changeto($amount, $currency); $amount_xmr2 = $this->changeto($amount, $currency);
$address = $this->address; $address = $this->address;
$payment_id = bin2hex(openssl_random_pseudo_bytes(32)); $payment_id = bin2hex(openssl_random_pseudo_bytes(8));
$uri = "monero:$address?amount=$amount?payment_id=$payment_id"; $uri = "monero:$address?amount=$amount?payment_id=$payment_id";
$array_integrated_address = $this->monero_daemon->make_integrated_address($payment_id);
// Generate a QR code // Generate a QR code
echo "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>"; echo "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>";
@ -206,9 +210,8 @@ class Monero_Gateway extends WC_Payment_Gateway
<img src='https://chart.googleapis.com/chart?cht=qr&chs=150x150&chl=" . $uri . "' class='img-responsive'> <img src='https://chart.googleapis.com/chart?cht=qr&chs=150x150&chl=" . $uri . "' class='img-responsive'>
</div> </div>
<div class='col-sm-9 col-md-9 col-lg-9' style='padding:10px;'> <div class='col-sm-9 col-md-9 col-lg-9' style='padding:10px;'>
Send <b>" . $amount_xmr2 . " XMR</b> to<br/><input type='text' class='form-control' value='" . $this->address . "'> Send <b>" . $amount_xmr2 . " XMR</b> to<br/><input type='text' class='form-control' value='" . $array_integrated_address["integrated_address"]."'>
or scan QR Code with your mobile device<br/><br/> or scan QR Code with your mobile device<br/><br/>
<input type='text' value='" . $payment_id . "'>
<small>If you don't know how to pay with monero, click instructions button. </small> <small>If you don't know how to pay with monero, click instructions button. </small>
</div> </div>
<div class='col-sm-12 col-md-12 col-lg-12'> <div class='col-sm-12 col-md-12 col-lg-12'>
@ -232,18 +235,15 @@ class Monero_Gateway extends WC_Payment_Gateway
</div> </div>
<div class='modal-body container'> <div class='modal-body container'>
<b>Paying with Monero</b> <b>Paying with Monero</b>
<p>If you don't have Monero, you can buy it in some markets. If you have, please follow instructions<p> <p>If you don't have Monero, you can buy it at a trusted exchange. If you already have some, please follow instructions</p>
<p>Scan QR Code with Monero App, if you don't have Monero App, please copy the url on your browser.. It will open Monero Wallet GUI. <p>Scan the QR code into your monero app or copy and paste the address above into your Monero Wallet</p>
Submit payment with a click and finish!
My english is so poor, please write me suggestions!! :D
</div> </div>
<div class='modal-footer'> <div class='modal-footer'>
<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button> <button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>";
";
} }
@ -276,10 +276,10 @@ class Monero_Gateway extends WC_Payment_Gateway
public function verify_payment(){ public function verify_payment(){
/* /*
* Algoritmo per verificare i pagamenti * fucntion for verifying payments
* 1. prendi l'ultima height disponibile * 1. Get the latest block height available
* 2. Get_Bulk_payments con il payment id generato prima (visualizzare a video un avviso per cui l'utente non dovrà aggiornare) * 2. Get_Bulk_payments with the first payment id generated
* 3. Verifica se esiste un pagamento con il payment id e se l'amount è aumentato (NOTA: Non serve verificare quanto è aumentato, il payment id è unico) * 3. Verify that a payment has been made with the given payment id
* *
@ -310,7 +310,6 @@ class Monero_Gateway extends WC_Payment_Gateway
}*/ }*/
} }
} }

View file

@ -1,155 +1,337 @@
<?php <?php
/** /**
* Monero PHP API * library.php
* @author SerHack *
* @version 0.1 * Written using the JSON RPC specification -
* @year 2017 * http://json-rpc.org/wiki/specification
* *
* @author Kacper Rowinski <krowinski@implix.com>
* http://implix.com
* Modified to work with monero-rpc wallet by Serhack and cryptochangements
*/ */
class Monero_Library
/* WORK IN PROGRESS */ {
protected $url = null, $is_debug = false, $parameters_structure = 'array';
class Monero_Payments{ protected $curl_options = array(
private $url; CURLOPT_CONNECTTIMEOUT => 8,
private $client; CURLOPT_TIMEOUT => 8
private $ip; );
private $port;
/**
* Start the connection with daemon
* @param $ip IP of Monero RPC
* @param $port Port of Monero RPC
*/
function __construct ($ip = '127.0.0.1', $port, $protocol = 'http'){
$this->ip = $ip;
$this->port = $port;
// I need to implement a sort of validating http or https
$this->url = $protocol.'://'.$ip.':'.$port.'/json_rpc';
$this->client = new jsonRPCClient($this->url);
}
/*
* Run a method or method + parameters
* @param $method Name of Method
*/
private function _run($method,$params = null) {
$result = $this->client->_run($method, $params);
return $result;
}
private function _transform($amount){
$new_amount = $amount * 100000000;
return $new_amount;
}
/** private $httpErrors = array(
* Print json (for api) 400 => '400 Bad Request',
* @return $json 401 => '401 Unauthorized',
*/ 403 => '403 Forbidden',
public function _print($json){ 404 => '404 Not Found',
$json_parsed = json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 405 => '405 Method Not Allowed',
echo $json_parsed; 406 => '406 Not Acceptable',
408 => '408 Request Timeout',
500 => '500 Internal Server Error',
502 => '502 Bad Gateway',
503 => '503 Service Unavailable'
);
public function __construct($pUrl)
{
$this->validate(false === extension_loaded('curl'), 'The curl extension must be loaded for using this class!');
$this->validate(false === extension_loaded('json'), 'The json extension must be loaded for using this class!');
$this->url = $pUrl;
} }
/** private function getHttpErrorMessage($pErrorNumber)
* Print Monero Address as JSON Array {
*/ return isset($this->httpErrors[$pErrorNumber]) ? $this->httpErrors[$pErrorNumber] : null;
public function address(){ }
public function setDebug($pIsDebug)
{
$this->is_debug = !empty($pIsDebug);
return $this;
}
/* public function setParametersStructure($pParametersStructure)
{
if (in_array($pParametersStructure, array('array', 'object')))
{
$this->parameters_structure = $pParametersStructure;
}
else
{
throw new UnexpectedValueException('Invalid parameters structure type.');
}
return $this;
} */
public function setCurlOptions($pOptionsArray)
{
if (is_array($pOptionsArray))
{
$this->curl_options = $pOptionsArray + $this->curl_options;
}
else
{
throw new InvalidArgumentException('Invalid options type.');
}
return $this;
}
private function request($pMethod, $pParams)
{
static $requestId = 0;
// generating uniuqe id per process
$requestId++;
// check if given params are correct
$this->validate(false === is_scalar($pMethod), 'Method name has no scalar value');
// $this->validate(false === is_array($pParams), 'Params must be given as array');
// send params as an object or an array
//$pParams = ($this->parameters_structure == 'object') ? $pParams[0] : array_values($pParams);
// Request (method invocation)
$request = json_encode(array('jsonrpc' => '2.0', 'method' => $pMethod, 'params' => $pParams, 'id' => $requestId));
// if is_debug mode is true then add url and request to is_debug
$this->debug('Url: ' . $this->url . "\r\n", false);
$this->debug('Request: ' . $request . "\r\n", false);
$responseMessage = $this->getResponse($request);
// if is_debug mode is true then add response to is_debug and display it
$this->debug('Response: ' . $responseMessage . "\r\n", true);
// decode and create array ( can be object, just set to false )
$responseDecoded = json_decode($responseMessage, true);
// check if decoding json generated any errors
$jsonErrorMsg = $this->getJsonLastErrorMsg();
$this->validate( !is_null($jsonErrorMsg), $jsonErrorMsg . ': ' . $responseMessage);
// check if response is correct
$this->validate(empty($responseDecoded['id']), 'Invalid response data structure: ' . $responseMessage);
$this->validate($responseDecoded['id'] != $requestId, 'Request id: ' . $requestId . ' is different from Response id: ' . $responseDecoded['id']);
if (isset($responseDecoded['error']))
{
$errorMessage = 'Request have return error: ' . $responseDecoded['error']['message'] . '; ' . "\n" .
'Request: ' . $request . '; ';
if (isset($responseDecoded['error']['data']))
{
$errorMessage .= "\n" . 'Error data: ' . $responseDecoded['error']['data'];
}
$this->validate( !is_null($responseDecoded['error']), $errorMessage);
}
return $responseDecoded['result'];
}
protected function & getResponse(&$pRequest)
{
// do the actual connection
$ch = curl_init();
if ( !$ch)
{
throw new RuntimeException('Could\'t initialize a cURL session');
}
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $pRequest);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if ( !curl_setopt_array($ch, $this->curl_options))
{
throw new RuntimeException('Error while setting curl options');
}
// send the request
$response = curl_exec($ch);
// check http status code
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (isset($this->httpErrors[$httpCode]))
{
throw new RuntimeException('Response Http Error - ' . $this->httpErrors[$httpCode]);
}
// check for curl error
if (0 < curl_errno($ch))
{
throw new RuntimeException('Unable to connect to '.$this->url . ' Error: ' . curl_error($ch));
}
// close the connection
curl_close($ch);
return $response;
}
public function validate($pFailed, $pErrMsg)
{
if ($pFailed)
{
throw new RuntimeException($pErrMsg);
}
}
protected function debug($pAdd, $pShow = false)
{
static $debug, $startTime;
// is_debug off return
if (false === $this->is_debug)
{
return;
}
// add
$debug .= $pAdd;
// get starttime
$startTime = empty($startTime) ? array_sum(explode(' ', microtime())) : $startTime;
if (true === $pShow and !empty($debug))
{
// get endtime
$endTime = array_sum(explode(' ', microtime()));
// performance summary
$debug .= 'Request time: ' . round($endTime - $startTime, 3) . ' s Memory usage: ' . round(memory_get_usage() / 1024) . " kb\r\n";
echo nl2br($debug);
// send output imidiately
flush();
// clean static
$debug = $startTime = null;
}
}
function getJsonLastErrorMsg()
{
if (!function_exists('json_last_error_msg'))
{
function json_last_error_msg()
{
static $errors = array(
JSON_ERROR_NONE => 'No error',
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded'
);
$error = json_last_error();
return array_key_exists($error, $errors) ? $errors[$error] : 'Unknown error (' . $error . ')';
}
}
// Fix PHP 5.2 error caused by missing json_last_error function
if (function_exists('json_last_error'))
{
return json_last_error() ? json_last_error_msg() : null;
}
else
{
return null;
}
}
public function _run($method,$params = null)
{
$result = $this->request($method, $params);
return $result; //the result is returned as an array
}
//prints result as json
public function _print($json)
{
$json_encoded = json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
echo $json_encoded;
}
/*
* The following functions can all be called to interact with the monero rpc wallet
* They will majority of them will return the result as an array
* Example: $daemon->address(); where $daemon is an instance of this class, will return the wallet address as string within an array
*/
public function address()
{
$address = $this->_run('getaddress'); $address = $this->_run('getaddress');
return $address; return $address;
} }
/* public function getbalance()
* Print Monero Balance as JSON Array {
*/
public function getbalance(){
$balance = $this->_run('getbalance'); $balance = $this->_run('getbalance');
return $balance; return $balance;
} }
/* public function getheight()
* Print Monero Height as JSON Array {
*/
public function getheight(){
$height = $this->_run('getheight'); $height = $this->_run('getheight');
return $height; return $height;
} }
/* public function incoming_transfer($type)
* Incoming Transfer {
* $type must be All
*/
public function incoming_transfer($type){
$incoming_parameters = array('transfer_type' => $type); $incoming_parameters = array('transfer_type' => $type);
$incoming_transfers = $this->_run('incoming_transfers', $incoming_parameters); $incoming_transfers = $this->_run('incoming_transfers', $incoming_parameters);
return $incoming_transfers; return $incoming_transfers;
} }
public function get_transfers($input_type, $input_value){ public function get_transfers($input_type, $input_value)
{
$get_parameters = array($input_type => $input_value); $get_parameters = array($input_type => $input_value);
$get_transfers = $this->_run('get_transfers', $get_parameters); $get_transfers = $this->_run('get_transfers', $get_parameters);
return $get_transfers; return $get_transfers;
} }
public function view_key(){ public function view_key()
{
$query_key = array('key_type' => 'view_key'); $query_key = array('key_type' => 'view_key');
$query_key_method = $this->_run('query_key', $query_key); $query_key_method = $this->_run('query_key', $query_key);
return $query_key_method; return $query_key_method;
} }
/* A payment id can be passed as a string /* A payment id can be passed as a string
A random payment id will be generatd if one is not given */ A random payment id will be generatd if one is not given */
public function make_integrated_address($payment_id){ public function make_integrated_address($payment_id)
{
$integrate_address_parameters = array('payment_id' => $payment_id); $integrate_address_parameters = array('payment_id' => $payment_id);
$integrate_address_method = $this->_run('make_integrated_address', $integrate_address_parameters); $integrate_address_method = $this->_run('make_integrated_address', $integrate_address_parameters);
return $integrate_address_method; return $integrate_address_method;
} }
public function split_integrated_address($integrated_address){ public function split_integrated_address($integrated_address)
{
if(!isset($integrated_address)){ if(!isset($integrated_address)){
echo "Error: Integrated_Address mustn't be null"; echo "Error: Integrated_Address mustn't be null";
} }
else{ else{
$split_params = array('integrated_address' => $integrated_address); $split_params = array('integrated_address' => $integrated_address);
$split_methods = $this->_run('split_integrated_address', $split_params); $split_methods = $this->_run('split_integrated_address', $split_params);
return $split_methods; return $split_methods;
} }
} }
public function make_uri($address, $amount, $recipient_name = null, $description = null){ public function make_uri($address, $amount, $recipient_name = null, $description = null)
// If I pass 1, it will be 1 xmr. Then {
$new_amount = $amount * 1000000000000; // If I pass 1, it will be 0.0000001 xmr. Then
$new_amount = $amount * 100000000;
$uri_params = array('address' => $address, 'amount' => $new_amount, 'payment_id' => '', 'recipient_name' => $recipient_name, 'tx_description' => $description); $uri_params = array('address' => $address, 'amount' => $new_amount, 'payment_id' => '', 'recipient_name' => $recipient_name, 'tx_description' => $description);
$uri = $this->_run('make_uri', $uri_params); $uri = $this->_run('make_uri', $uri_params);
return $uri; return $uri;
} }
public function parse_uri($uri)
public function parse_uri($uri){ {
$uri_parameters = array('uri' => $uri); $uri_parameters = array('uri' => $uri);
$parsed_uri = $this->_run('parse_uri', $uri_parameters); $parsed_uri = $this->_run('parse_uri', $uri_parameters);
return $parsed_uri; return $parsed_uri;
} }
public function transfer($amount, $address, $mixin = 4){ public function transfer($amount, $address, $mixin = 4)
{
$new_amount = $amount * 1000000000000; $new_amount = $amount * 1000000000000;
$destinations = array('amount' => $new_amount, 'address' => $address); $destinations = array('amount' => $new_amount, 'address' => $address);
$transfer_parameters = array('destinations' => array($destinations), 'mixin' => $mixin, 'get_tx_key' => true, 'unlock_time' => 0, 'payment_id' => ''); $transfer_parameters = array('destinations' => array($destinations), 'mixin' => $mixin, 'get_tx_key' => true, 'unlock_time' => 0, 'payment_id' => '');
$transfer_method = $this->_run('transfer', $transfer_parameters); $transfer_method = $this->_run('transfer', $transfer_parameters);
return $transfer_method; return $transfer_method;
} }
public function get_payments($payment_id){ public function get_payments($payment_id)
$get_payments_parameters = array('payment_id' => $payment_id); {
$get_payments = $this->_run('get_payments', $get_payments_parameters); $get_payments_parameters = array('payment_id' => $payment_id);
return $get_payments; $get_payments = $this->_run('get_payments', $get_payments_parameters);
} return $get_payments;
}
public function get_bulk_payments($payment_id, $min_block_height){
public function get_bulk_payments($payment_id, $min_block_height)
{
$get_bulk_payments_parameters = array('payment_id' => $payment_id, 'min_block_height' => $min_block_height); $get_bulk_payments_parameters = array('payment_id' => $payment_id, 'min_block_height' => $min_block_height);
$get_bulk_payments = $this->_run('get_bulk_payments', $get_bulk_payments_parameters); $get_bulk_payments = $this->_run('get_bulk_payments', $get_bulk_payments_parameters);
return $get_bulk_payments; return $get_bulk_payments;
} }
} }