File: /home/confeduphaar/backip-old-files/plugins/vmpayment/eway/library/src/Rapid/Client.php
<?php
/**
* @version $Id: Client.php 9790 2018-03-12 14:53:26Z alatak $
* @package VirtueMart
* @subpackage Plugins - Eway
* @package VirtueMart
* @subpackage Payment
* @link https://virtuemart.net
*
* @copyright Copyright (c) 2015 Web Active Corporation Pty Ltd
*
* @license MIT License GNU/GPL, see LICENSE.php
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
*/
namespace Eway\Rapid;
use Eway\Rapid\Contract\Client as ClientContract;
use Eway\Rapid\Contract\Http\ResponseInterface;
use Eway\Rapid\Contract\HttpService as HttpServiceContract;
use Eway\Rapid\Enum\ApiMethod;
use Eway\Rapid\Enum\PaymentMethod;
use Eway\Rapid\Enum\TransactionType;
use Eway\Rapid\Enum\LogLevel;
use Eway\Rapid\Exception\MassAssignmentException;
use Eway\Rapid\Exception\MethodNotImplementedException;
use Eway\Rapid\Exception\RequestException;
use Eway\Rapid\Model\Customer;
use Eway\Rapid\Model\Refund;
use Eway\Rapid\Model\Response\AbstractResponse;
use Eway\Rapid\Model\Response\CreateCustomerResponse;
use Eway\Rapid\Model\Response\CreateTransactionResponse;
use Eway\Rapid\Model\Response\QueryAccessCodeResponse;
use Eway\Rapid\Model\Response\QueryCustomerResponse;
use Eway\Rapid\Model\Response\QueryTransactionResponse;
use Eway\Rapid\Model\Response\RefundResponse;
use Eway\Rapid\Model\Response\SettlementSearchResponse;
use Eway\Rapid\Model\Transaction;
use Eway\Rapid\Service\Http;
use Eway\Rapid\Validator\ClassValidator;
use Eway\Rapid\Validator\EnumValidator;
use InvalidArgumentException;
/**
* eWAY Rapid Client
*
* Connect to eWAY's Rapid API to process transactions and refunds, create and
* update customer tokens.
*
*/
class Client implements ClientContract
{
/**
* Rapid API Key
*
* @var string
*/
private $apiKey;
/**
* Password for the API Key
*
* @var string
*/
private $apiPassword;
/**
* Possible values ("Production", "Sandbox", or a URL) "Production" and "Sandbox"
* will default to the Global Rapid API Endpoints.
*
* @var string
*/
private $endpoint;
/**
* The eWAY Rapid API version to be used.
*
*
* @var string
*/
private $version;
/**
* True if the Client has a valid API Key, Password and Endpoint Set.
*
* @var bool
*/
private $isValid = false;
/**
* Contains the Rapid Error code in the case of initialisation errors.
*
* @var array
*/
private $errors = [];
/**
* @var HttpServiceContract
*/
private $httpService;
/**
* @var Psr\Log\LoggerInterface
*/
private $logger;
/**
* @param string $apiKey
* @param string $apiPassword
* @param string $endpoint
* @param Psr\Log\LoggerInterface $logger PSR-3 logger
*/
public function __construct($apiKey, $apiPassword, $endpoint, $logger = null)
{
if (isset($logger)) {
$this->setLogger($logger);
}
$this->setHttpService(new Http());
$this->setCredential($apiKey, $apiPassword);
$this->setEndpoint($endpoint);
}
#region Public Functions
/**
* @inheritdoc
*/
public function isValid()
{
return $this->isValid;
}
/**
* @inheritdoc
*/
public function getErrors()
{
return $this->errors;
}
/**
* @inheritdoc
*/
public function getEndpoint()
{
return $this->endpoint;
}
/**
* @inheritdoc
*/
public function setEndpoint($endpoint)
{
if (ClientContract::MODE_SANDBOX === strtolower($endpoint)) {
$endpoint = ClientContract::ENDPOINT_SANDBOX;
} elseif (ClientContract::MODE_PRODUCTION === strtolower($endpoint)) {
$endpoint = ClientContract::ENDPOINT_PRODUCTION;
}
$this->endpoint = $endpoint;
$this->getHttpService()->setBaseUrl($endpoint);
$this->validateEndpoint();
return $this;
}
/**
* @inheritdoc
*/
public function setVersion($version)
{
$this->version = $version;
$this->getHttpService()->setVersion($version);
return $this;
}
/**
* @inheritdoc
*/
public function setCredential($apiKey, $apiPassword)
{
$this->apiKey = $apiKey;
$this->apiPassword = $apiPassword;
$this->getHttpService()->setKey($apiKey);
$this->getHttpService()->setPassword($apiPassword);
$this->validateCredentials();
return $this;
}
/**
* @inheritdoc
*/
public function setLogger($logger)
{
$this->logger = $logger;
return $this;
}
/**
* @inheritdoc
*/
public function createTransaction($apiMethod, $transaction)
{
return $this->invoke(CreateTransactionResponse::getClass());
}
/**
* @inheritdoc
*/
public function queryTransaction($reference)
{
return $this->invoke(QueryTransactionResponse::getClass());
}
/**
* @inheritdoc
*/
public function queryInvoiceNumber($invoiceNumber)
{
return $this->invoke(QueryTransactionResponse::getClass());
}
/**
* @inheritdoc
*/
public function queryInvoiceReference($invoiceReference)
{
return $this->invoke(QueryTransactionResponse::getClass());
}
/**
* @inheritdoc
*/
public function createCustomer($apiMethod, $customer)
{
return $this->invoke(CreateCustomerResponse::getClass());
}
/**
* @inheritdoc
*/
public function updateCustomer($apiMethod, $customer)
{
return $this->invoke(CreateCustomerResponse::getClass());
}
/**
* @inheritdoc
*/
public function queryCustomer($tokenCustomerId)
{
return $this->invoke(QueryCustomerResponse::getClass());
}
/**
* @inheritdoc
*/
public function refund($refund)
{
return $this->invoke(RefundResponse::getClass());
}
/**
* @inheritdoc
*/
public function cancelTransaction($transactionId)
{
return $this->invoke(RefundResponse::getClass());
}
/**
* @param mixed $accessCode
*
* @return QueryAccessCodeResponse
*/
public function queryAccessCode($accessCode)
{
return $this->invoke(QueryAccessCodeResponse::getClass());
}
/**
* @inheritdoc
*/
public function settlementSearch($query)
{
return $this->invoke(SettlementSearchResponse::getClass());
}
#endregion
#region Getter/Setter
/**
* @param HttpServiceContract $httpService
*
* @return Client
*/
public function setHttpService(HttpServiceContract $httpService)
{
$this->httpService = $httpService;
return $this;
}
/**
* @return HttpServiceContract
*/
public function getHttpService()
{
return $this->httpService;
}
#endregion
#region Internal logic
/**
* @param $apiMethod
* @param $transaction
*
* @return ResponseInterface
*/
private function doCreateTransaction($apiMethod, $transaction)
{
$apiMethod = EnumValidator::validate('Eway\Rapid\Enum\ApiMethod', 'ApiMethod', $apiMethod);
/** @var Transaction $transaction */
$transaction = ClassValidator::getInstance('Eway\Rapid\Model\Transaction', $transaction);
switch ($apiMethod) {
case ApiMethod::DIRECT:
case ApiMethod::WALLET:
if ($transaction->Capture) {
$transaction->Method = PaymentMethod::PROCESS_PAYMENT;
} else {
$transaction->Method = PaymentMethod::AUTHORISE;
}
return $this->getHttpService()->postTransaction($transaction->toArray());
case ApiMethod::RESPONSIVE_SHARED:
if ($transaction->Capture) {
if ((isset($transaction->Customer) && isset($transaction->Customer->TokenCustomerID))
|| (isset($transaction->SaveCustomer) && $transaction->SaveCustomer == true)) {
$transaction->Method = PaymentMethod::TOKEN_PAYMENT;
} else {
$transaction->Method = PaymentMethod::PROCESS_PAYMENT;
}
} else {
$transaction->Method = PaymentMethod::AUTHORISE;
}
return $this->getHttpService()->postAccessCodeShared($transaction->toArray());
case ApiMethod::TRANSPARENT_REDIRECT:
if ($transaction->Capture) {
if ((isset($transaction->Customer) && isset($transaction->Customer->TokenCustomerID))
|| (isset($transaction->SaveCustomer) && $transaction->SaveCustomer == true)) {
$transaction->Method = PaymentMethod::TOKEN_PAYMENT;
} else {
$transaction->Method = PaymentMethod::PROCESS_PAYMENT;
}
} else {
$transaction->Method = PaymentMethod::AUTHORISE;
}
return $this->getHttpService()->postAccessCode($transaction->toArray());
case ApiMethod::AUTHORISATION:
return $this->getHttpService()->postCapturePayment($transaction->toArray());
default:
// Although right now this code is not reachable, protect against incomplete
// changes to ApiMethod
throw new MethodNotImplementedException();
}
}
/**
* @param $reference
*
* @return ResponseInterface
*/
private function doQueryTransaction($reference)
{
return $this->getHttpService()->getTransaction($reference);
}
/**
* @param $invoiceNumber
*
* @return ResponseInterface
*/
private function doQueryInvoiceNumber($invoiceNumber)
{
return $this->getHttpService()->getTransactionInvoiceNumber($invoiceNumber);
}
/**
* @param $invoiceReference
*
* @return ResponseInterface
*/
private function doQueryInvoiceReference($invoiceReference)
{
return $this->getHttpService()->getTransactionInvoiceReference($invoiceReference);
}
/**
* @param $apiMethod
* @param $customer
*
* @return ResponseInterface
*/
private function doCreateCustomer($apiMethod, $customer)
{
/** @var Customer $customer */
$customer = ClassValidator::getInstance('Eway\Rapid\Model\Customer', $customer);
$apiMethod = EnumValidator::validate('Eway\Rapid\Enum\ApiMethod', 'ApiMethod', $apiMethod);
$transaction = $this->customerToTransaction($customer);
$transaction->Method = PaymentMethod::CREATE_TOKEN_CUSTOMER;
switch ($apiMethod) {
case ApiMethod::DIRECT:
return $this->getHttpService()->postTransaction($transaction->toArray());
case ApiMethod::RESPONSIVE_SHARED:
$transaction->Payment = ['TotalAmount' => 0];
return $this->getHttpService()->postAccessCodeShared($transaction->toArray());
case ApiMethod::TRANSPARENT_REDIRECT:
$transaction->Payment = ['TotalAmount' => 0];
return $this->getHttpService()->postAccessCode($transaction->toArray());
default:
// Although right now this code is not reachable, protect against incomplete
// changes to ApiMethod
throw new MethodNotImplementedException();
}
}
/**
*
* @param $apiMethod
* @param type $customer
* @return ResponseInterface
* @throws MethodNotImplementedException
*/
private function doUpdateCustomer($apiMethod, $customer)
{
/** @var Customer $customer */
$customer = ClassValidator::getInstance('Eway\Rapid\Model\Customer', $customer);
$apiMethod = EnumValidator::validate('Eway\Rapid\Enum\ApiMethod', 'ApiMethod', $apiMethod);
$transaction = $this->customerToTransaction($customer);
$transaction->Method = PaymentMethod::UPDATE_TOKEN_CUSTOMER;
$transaction->Payment = ['TotalAmount' => 0];
switch ($apiMethod) {
case ApiMethod::DIRECT:
return $this->getHttpService()->postTransaction($transaction->toArray());
case ApiMethod::RESPONSIVE_SHARED:
return $this->getHttpService()->postAccessCodeShared($transaction->toArray());
case ApiMethod::TRANSPARENT_REDIRECT:
return $this->getHttpService()->postAccessCode($transaction->toArray());
default:
// Although right now this code is not reachable, protect against incomplete
// changes to ApiMethod
throw new MethodNotImplementedException();
}
}
/**
* @param $tokenCustomerId
*
* @return ResponseInterface
*/
private function doQueryCustomer($tokenCustomerId)
{
return $this->getHttpService()->getCustomer($tokenCustomerId);
}
/**
* @param $refund
*
* @return ResponseInterface
*/
private function doRefund($refund)
{
/** @var Refund $refund */
$refund = ClassValidator::getInstance('Eway\Rapid\Model\Refund', $refund);
return $this->getHttpService()->postTransactionRefund($refund->Refund->TransactionID, $refund->toArray());
}
/**
* @param $transactionId
*
* @return ResponseInterface
*/
private function doCancelTransaction($transactionId)
{
$refund = [
'TransactionID' => $transactionId,
];
/** @var Refund $refund */
$refund = ClassValidator::getInstance('Eway\Rapid\Model\Refund', $refund);
return $this->getHttpService()->postCancelAuthorisation($refund->toArray());
}
/**
* @param $accessCode
*
* @return ResponseInterface
*/
private function doQueryAccessCode($accessCode)
{
return $this->getHttpService()->getAccessCode($accessCode);
}
/**
* @param $query
*
* @return ResponseInterface
*/
private function doSettlementSearch($query)
{
$search = ClassValidator::getInstance('Eway\Rapid\Model\SettlementSearch', $query);
return $this->getHttpService()->getSettlementSearch($search->toArray());
}
#endregion
#region Internal helpers
/**
* @param $responseClass
*
* @return AbstractResponse
*/
private function invoke($responseClass)
{
if (!$this->isValid()) {
return $this->getErrorResponse($responseClass);
}
try {
$caller = $this->getCaller();
$response = call_user_func_array([$this, 'do'.ucfirst($caller['function'])], $caller['args']);
return $this->wrapResponse($responseClass, $response);
} catch (InvalidArgumentException $e) {
$this->addError(self::ERROR_INVALID_ARGUMENT);
} catch (MassAssignmentException $e) {
$this->addError(self::ERROR_INVALID_ARGUMENT);
}
return $this->getErrorResponse($responseClass);
}
/**
* @return mixed
*/
private function getCaller()
{
$callers = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3);
// Index of the caller of this function is 1
// So caller of that caller is 2
return $callers[2];
}
/**
* @param string $class
* @param ResponseInterface $httpResponse
*
* @return mixed
*/
private function wrapResponse($class, $httpResponse = null)
{
$data = [];
try {
if (isset($httpResponse)) {
$this->checkResponse($httpResponse);
$body = (string)$httpResponse->getBody();
if (!$this->isJson($body)) {
$this->log('error', "Response is not valid JSON");
$this->addError(self::ERROR_INVALID_JSON);
} else {
$data = json_decode($body, true);
}
} else {
$this->log('error', "Response from gateway is empty");
$this->addError(self::ERROR_EMPTY_RESPONSE);
}
} catch (RequestException $e) {
// An error code is already provided by checkResponse
}
/** @var AbstractResponse $response */
$response = new $class($data);
foreach ($this->getErrors() as $errorCode) {
$response->addError($errorCode);
}
return $response;
}
/**
* @param $string
*
* @return bool
*/
private function isJson($string)
{
return is_string($string) && is_object(json_decode($string)) && (json_last_error() == JSON_ERROR_NONE);
}
/**
* @return $this
*/
private function emptyErrors()
{
$this->errors = [];
return $this;
}
/**
*
* @return $this
*/
public function validateCredentials()
{
$this->removeError(self::ERROR_INVALID_CREDENTIAL);
if (empty($this->apiKey) || empty($this->apiPassword)) {
$this->log('error', "Missing API key or password");
$this->addError(self::ERROR_INVALID_CREDENTIAL, false);
}
if (empty($this->errors)) {
$this->isValid = true;
}
return $this;
}
/**
*
* @return $this
*/
public function validateEndpoint()
{
$this->removeError(self::ERROR_INVALID_ENDPOINT);
if (empty($this->endpoint)
|| strpos($this->endpoint, 'https') !== 0
|| substr($this->endpoint, -1) != '/') {
$this->log('error', "Missing or invalid endpoint");
$this->addError(self::ERROR_INVALID_ENDPOINT, false);
}
if (empty($this->errors)) {
$this->isValid = true;
}
return $this;
}
/**
* @param string $errorCode
*
* @return $this
*/
private function addError($errorCode, $valid = true)
{
$this->isValid = $valid;
$this->errors[] = $errorCode;
return $this;
}
/**
* @param string $errorCode
*
* @return $this
*/
private function removeError($errorCode)
{
$this->errors = array_diff($this->errors, [$errorCode]);
return $this;
}
/**
* @param $responseClass
*
* @return mixed
*/
private function getErrorResponse($responseClass)
{
$data = ['Errors' => implode(',', $this->getErrors())];
return new $responseClass($data);
}
/**
* @param ResponseInterface $response
*
* @throws RequestException
*/
private function checkResponse($response)
{
$hasRequestError = false;
if (preg_match('/4\d\d/', $response->getStatusCode())) {
$this->log('error', "Invalid API key or password");
$this->addError(self::ERROR_HTTP_AUTHENTICATION_ERROR, false);
$hasRequestError = true;
} elseif (preg_match('/5\d\d/', $response->getStatusCode())) {
$this->log('error', "Gateway error - HTTP " . $response->getStatusCode());
$this->addError(self::ERROR_HTTP_SERVER_ERROR);
$hasRequestError = true;
} elseif ($response->getError()) {
$this->log('error', "Connection error: " . $response->getError());
$this->addError(self::ERROR_CONNECTION_ERROR);
$hasRequestError = true;
}
if ($hasRequestError) {
throw new RequestException(sprintf("Last HTTP response status code: %s", $response->getStatusCode()));
}
}
/**
* Convert a Customer to a Transaction object for a create or update
* token transaction
*
* @param Eway\Rapid\Model\Customer $customer
* @return Eway\Rapid\Model\Transaction
*/
private function customerToTransaction($customer)
{
$transaction = [
'Customer' => $customer->toArray(),
'TransactionType' => TransactionType::PURCHASE, // ALATAK
];
foreach ($customer->toArray() as $key => $value) {
if ($key != 'TokenCustomerID') {
$transaction[$key] = $value;
}
}
/** @var Transaction $transaction */
return ClassValidator::getInstance('Eway\Rapid\Model\Transaction', $transaction);
}
/**
*
* @param string $level
* @param string $message
*/
private function log($level, $message)
{
if (isset($this->logger) && LogLevel::isValidValue($level)) {
$this->logger->$level($message);
}
}
#endregion
}