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 }