<?php


namespace PayU\MerchantClientApi;


/**
 * PHP API class for PayU Token
 *
 * @package PayU\MerchantClientApi
 */
class TokenService
{
    
/**
     * @var string
     */
    
private $endpointUrl;

    
/**
     * @var string
     */
    
private $merchantCode;

    
/**
     * @var string
     */
    
private $merchantKey;

    
/**
     * @var resource
     */
    
private $curl;

    
/**
     * Class constructor
     *
     * @param string $endpointUrl Endpoint URL for API calls mentioned in documentation
     * @param string $merchantCode Merchant code
     * @param string $merchantKey Merchant key
     */
    
public function __construct($endpointUrl$merchantCode$merchantKey)
    {
        
$this->endpointUrl $endpointUrl;
        
$this->merchantCode $merchantCode;
        
$this->merchantKey $merchantKey;
    }

    
/**
     * Creates a new sale using the token
     *
     * @param string $code The token number
     * @param float|int $amount New order amount
     * @param string $currency Price currency
     * @param string $externalRef Merchant Reference Number for the transaction
     * @param array $additionalData
     * @return mixed
     */
    
public function newSale($code$amount$currency$externalRef, array $additionalData = array())
    {
        
$data = array(
            
'REF_NO' => $code,
            
'METHOD' => 'TOKEN_NEWSALE',
            
'AMOUNT' => $amount,
            
'CURRENCY' => $currency,
            
'EXTERNAL_REF' => $externalRef,
        );
        return 
$this->request(
            
array_merge($data$additionalData)
        );
    }

    
/**
     * Executes the request to PayU service
     *
     * @param array $params
     * @return mixed
     */
    
private function request(array $params)
    {
        
$result $this->runCurl($this->endpointUrl$params);

        return 
$this->decodeResponse($result);
    }

    
/**
     * @param string $url
     * @param array $params
     * @return bool|string
     * @throws \Exception
     */
    
private function runCurl($url, array $params)
    {
        if (!
is_resource($this->curl)) {
            
$this->curl curl_init($url);

            
curl_setopt_array(
                
$this->curl,
                array(
                    
CURLOPT_RETURNTRANSFER => true,
                    
CURLOPT_SSL_VERIFYPEER => false,
                    
CURLOPT_SSL_VERIFYHOST => 2,
                    
CURLOPT_FOLLOWLOCATION => false,
                    
CURLOPT_USERAGENT => __CLASS__,
                    
CURLOPT_POST => true,
                )
            );
        }

        
$params array_merge(
            
$params,
            array(
                
'MERCHANT' => $this->merchantCode,
                
'TIMESTAMP' => gmdate('YmdHis'),
            )
        );

        unset(
$params['SIGN']);

        
$params['SIGN'] = $this->calculateSignature($params);

        
curl_setopt($this->curlCURLOPT_POSTFIELDS$params);

        
$result curl_exec($this->curl);

        if (
false === $result) {
            throw new 
\Exception('Curl failed: ' curl_error($this->curl));
        }

        
$httpCode curl_getinfo($this->curlCURLINFO_HTTP_CODE);

        
curl_close($this->curl);

        if (
200 != $httpCode) {
            throw new 
\Exception('Unexpected HTTP code: ' $httpCode);
        }

        return 
$result;
    }

    
/**
     * Calculates the signature
     *
     * @param array $params
     * @return string
     */
    
private function calculateSignature(array $params)
    {
        
ksort($params);
        
$hashString '';

        foreach (
$params as $v) {
            
$hashString .= strlen($v) . $v;
        }

        return 
hash_hmac('md5'$hashString$this->merchantKey);
    }

    
/**
     * @param string $result
     * @return mixed
     * @throws \Exception
     */
    
private function decodeResponse($result)
    {
        
$retval json_decode($resulttrue);
        if (
JSON_ERROR_NONE !== json_last_error()) {
            throw new 
\Exception('JSON decode error');
        }
        return 
$retval;
    }

    
/**
     * Get info about the token
     *
     * @param string $code The token number
     * @return mixed
     */
    
public function getInfo($code)
    {
        return 
$this->request(
            array(
                
'REF_NO' => $code,
                
'METHOD' => 'TOKEN_GETINFO',
            )
        );
    }

    
/**
     * Cancel the token
     *
     * @param string $code The token number
     * @param string $reason Reason for cancelling the order
     * @return mixed
     */
    
public function cancel($code$reason)
    {
        return 
$this->request(
            array(
                
'REF_NO' => $code,
                
'METHOD' => 'TOKEN_CANCEL',
                
'CANCEL_REASON' => $reason,
            )
        );
    }

    
/**
     * Persist the token
     *
     * @param string $code The token number
     * @return mixed
     */
    
public function persist($code)
    {
        return 
$this->request(
            array(
                
'REF_NO' => $code,
                
'METHOD' => 'TOKEN_PERSIST',
            )
        );
    }

    
/**
     * Frees the resource when the object is destroyed
     */
    
public function __destruct()
    {
        if (
is_resource($this->curl)) {
            
curl_close($this->curl);
        }
    }
}