<?php
namespace Payments;

/**
 * This class is used to generate payments via ePay.bg and to process the payment notifications.
 * @package Sami's ePay class
 * @version $Id: ePay.php v.1.0 2019-03-25 16:06:00 $
 * @author Samuil Banti https://www.samiwell.eu/
 * @copyright (C) 2019 - Samuil Banti
 * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
**/
class ePay
{

    /**
    * @var - The ePay settings.
    */
    private $Settings;
    
    /**
    * @var - The URL for the cURL requests.
    */
    private $curlURL;
    
    /**
    * Set the ePay settings.
    * @param object $Settings - The ePay settings as described in the ePaySettings settings.
    */
    public function __construct(object $Settings)
    {
        $this->Settings = $Settings;
    }
    
    /**
    * Initiate a new payment.
    * @param array $paymentData - The data needed to do the payment. It's described in the ePay documentation.
    * @return string $responseData - The response provided by ePay.
    */
    public function sendPayment(array $paymentData)
    {
        $this->curlURL = $this->Settings->PAYMENT_URL;
        $responseData = $this->sendRequest($paymentData);
        return $responseData;
    }
    
    /**
    * Decode an ePay notification.
    * @param array $notification - The notification received by ePay. It should contain ENCODED and CHECKSUM parameters.
    * @return array $response - The data provided by ePay.
    */
    public function decodeNotification(array $notification)
    {
        if ( !isset($notification['ENCODED']) || !isset($notification['CHECKSUM']) ) {
            return $this->setError('The notification does NOT contain all data!');
        }
        $checksum = $this->calculateChecksum($notification['ENCODED']);
        if ($checksum != $notification['CHECKSUM']) {
            return $this->setError('The checksum does NOT match!');
        }
        
        $arguments = base64_decode($notification['ENCODED']);
        $arguments = preg_split('/\r\n|\r|\n/', $arguments);
        
        $response = [];
        foreach($arguments as $row) {
            list($key, $value) = explode('=', $row);
            $response[$key] = $value;
        }
        return $response;
    }
    
    /**
    * Send request to ePay.
    * @param array $arguments - The variables that will be encoded in the request.
    * @return string $responseData - The response provided by ePay.
    */
    private function sendRequest(array $arguments)
    {
        $encodedArguments = 'MIN=' . $this->Settings->MIN . PHP_EOL;
        foreach($arguments as $key => $value) {
            $encodedArguments .= $key . '=' . $value . PHP_EOL;
        }
        $encodedArguments = trim($encodedArguments, PHP_EOL);
        
        $encodedArguments = base64_encode($encodedArguments);
        $checksum = $this->calculateChecksum($encodedArguments);
        
        $curlArguments = http_build_query([
            'ENCODED' => $encodedArguments,
            'CHECKSUM' => $checksum,
        ]);
        
        $curlURL = rtrim($this->curlURL, '?') . '?' . $curlArguments;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $curlURL);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Should be set to true in production
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $responseData = curl_exec($ch);
        
        if(curl_errno($ch)) {
            return curl_error($ch);
        }
        curl_close($ch);
        return $responseData;
    }
    
    /**
    * Calculate ePay checksum
    * @param string $encodedArguments - Base 64 encoded list of parameters.
    */
    private function calculateChecksum(string $encodedArguments)
    {
        $checksum = hash_hmac('sha1', $encodedArguments, $this->Settings->SECRET);
        return $checksum;
    }
    
    /**
    * Handle potential errors.
    * NOTE: The concept is to be overwriten by child class in case of need.
    * @param string $error - The triggered error.
    * @return NULL
    */
    protected function setError(string $error)
    {
        trigger_error($error);
        return;    
    }
    
}
