diff --git a/monero/include/monero_payments.php b/monero/include/monero_payments.php index 7006b50..4954505 100644 --- a/monero/include/monero_payments.php +++ b/monero/include/monero_payments.php @@ -3,7 +3,8 @@ class Monero_Gateway extends WC_Payment_Gateway { - + private $monero_daemon; + function __construct() { @@ -53,6 +54,7 @@ class Monero_Gateway extends WC_Payment_Gateway 'process_admin_options' )); } + $this->monero_daemon = new Monero_Library($this->host . ':' . $this->port . '/json_rpc'); } public function admin_options() @@ -136,7 +138,8 @@ class Monero_Gateway extends WC_Payment_Gateway { $xmr_live_price = $this->retriveprice($currency); $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 false; - } - + } public function instruction($order_id) { @@ -188,8 +190,10 @@ class Monero_Gateway extends WC_Payment_Gateway $currency = $order->currency; $amount_xmr2 = $this->changeto($amount, $currency); $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"; + $array_integrated_address = $this->monero_daemon->make_integrated_address($payment_id); + // Generate a QR code echo ""; @@ -206,9 +210,8 @@ class Monero_Gateway extends WC_Payment_Gateway
- Send " . $amount_xmr2 . " XMR to
+ Send " . $amount_xmr2 . " XMR to
or scan QR Code with your mobile device

- If you don't know how to pay with monero, click instructions button.
@@ -232,18 +235,15 @@ class Monero_Gateway extends WC_Payment_Gateway
- - "; + "; } @@ -276,10 +276,10 @@ class Monero_Gateway extends WC_Payment_Gateway public function verify_payment(){ /* - * Algoritmo per verificare i pagamenti - * 1. prendi l'ultima height disponibile - * 2. Get_Bulk_payments con il payment id generato prima (visualizzare a video un avviso per cui l'utente non dovrà aggiornare) - * 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) + * fucntion for verifying payments + * 1. Get the latest block height available + * 2. Get_Bulk_payments with the first payment id generated + * 3. Verify that a payment has been made with the given payment id * @@ -310,7 +310,6 @@ class Monero_Gateway extends WC_Payment_Gateway }*/ - } - + } } diff --git a/monero/library.php b/monero/library.php index 54585e8..fb0ec80 100644 --- a/monero/library.php +++ b/monero/library.php @@ -1,155 +1,337 @@ + * http://implix.com + * Modified to work with monero-rpc wallet by Serhack and cryptochangements */ - - /* WORK IN PROGRESS */ - - class Monero_Payments{ - private $url; - private $client; - private $ip; - private $port; +class Monero_Library +{ + protected $url = null, $is_debug = false, $parameters_structure = 'array'; + protected $curl_options = array( + CURLOPT_CONNECTTIMEOUT => 8, + CURLOPT_TIMEOUT => 8 + ); - /** - * 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; - } - /** - * Print json (for api) - * @return $json - */ - public function _print($json){ - $json_parsed = json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - echo $json_parsed; + private $httpErrors = array( + 400 => '400 Bad Request', + 401 => '401 Unauthorized', + 403 => '403 Forbidden', + 404 => '404 Not Found', + 405 => '405 Method Not Allowed', + 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; } - - /** - * Print Monero Address as JSON Array - */ - public function address(){ + + private function getHttpErrorMessage($pErrorNumber) + { + return isset($this->httpErrors[$pErrorNumber]) ? $this->httpErrors[$pErrorNumber] : null; + } + + 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'); return $address; } - - /* - * Print Monero Balance as JSON Array - */ - public function getbalance(){ + + public function getbalance() + { $balance = $this->_run('getbalance'); return $balance; - } - - /* - * Print Monero Height as JSON Array - */ - public function getheight(){ + } + + public function getheight() + { $height = $this->_run('getheight'); return $height; - } - - /* - * Incoming Transfer - * $type must be All - */ - public function incoming_transfer($type){ + } + + public function incoming_transfer($type) + { $incoming_parameters = array('transfer_type' => $type); $incoming_transfers = $this->_run('incoming_transfers', $incoming_parameters); 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_transfers = $this->_run('get_transfers', $get_parameters); return $get_transfers; } - - public function view_key(){ + + public function view_key() + { $query_key = array('key_type' => 'view_key'); $query_key_method = $this->_run('query_key', $query_key); return $query_key_method; } - /* A payment id can be passed as a string - A random payment id will be generatd if one is not given */ - public function make_integrated_address($payment_id){ + /* A payment id can be passed as a string + A random payment id will be generatd if one is not given */ + public function make_integrated_address($payment_id) + { $integrate_address_parameters = array('payment_id' => $payment_id); $integrate_address_method = $this->_run('make_integrated_address', $integrate_address_parameters); return $integrate_address_method; - } - - public function split_integrated_address($integrated_address){ + } + + public function split_integrated_address($integrated_address) + { if(!isset($integrated_address)){ echo "Error: Integrated_Address mustn't be null"; } else{ - $split_params = array('integrated_address' => $integrated_address); - $split_methods = $this->_run('split_integrated_address', $split_params); - return $split_methods; + $split_params = array('integrated_address' => $integrated_address); + $split_methods = $this->_run('split_integrated_address', $split_params); + return $split_methods; } } - - 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; + + public function make_uri($address, $amount, $recipient_name = null, $description = null) + { + // 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); return $uri; } - - - public function parse_uri($uri){ - $uri_parameters = array('uri' => $uri); + + public function parse_uri($uri) + { + $uri_parameters = array('uri' => $uri); $parsed_uri = $this->_run('parse_uri', $uri_parameters); return $parsed_uri; } - - public function transfer($amount, $address, $mixin = 4){ + + public function transfer($amount, $address, $mixin = 4) + { $new_amount = $amount * 1000000000000; $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_method = $this->_run('transfer', $transfer_parameters); return $transfer_method; } - - public function get_payments($payment_id){ - $get_payments_parameters = array('payment_id' => $payment_id); - $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_payments($payment_id) + { + $get_payments_parameters = array('payment_id' => $payment_id); + $get_payments = $this->_run('get_payments', $get_payments_parameters); + return $get_payments; + } + + 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 = $this->_run('get_bulk_payments', $get_bulk_payments_parameters); return $get_bulk_payments; - } -} + } +}