File indexing completed on 2024-12-22 05:36:23

0001 <?php
0002 /**
0003  *  ocs-webserver
0004  *
0005  *  Copyright 2016 by pling GmbH.
0006  *
0007  *    This file is part of ocs-webserver.
0008  *
0009  *    This program is free software: you can redistribute it and/or modify
0010  *    it under the terms of the GNU Affero General Public License as
0011  *    published by the Free Software Foundation, either version 3 of the
0012  *    License, or (at your option) any later version.
0013  *
0014  *    This program is distributed in the hope that it will be useful,
0015  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017  *    GNU Affero General Public License for more details.
0018  *
0019  *    You should have received a copy of the GNU Affero General Public License
0020  *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021  **/
0022 
0023 abstract class Local_Payment_Dwolla_Callback
0024 {
0025 
0026     /** @var \Zend_Config */
0027     protected $_config;
0028     /** @var \Zend_Log */
0029     protected $_logger;
0030     /** @var  array */
0031     protected $_dataCallback;
0032     /** @var  Local_Payment_Dwolla_ResponseInterface */
0033     protected $_transactionMessage;
0034 
0035 
0036     /**
0037      * @param $config
0038      * @param null $logger
0039      * @throws Exception
0040      */
0041     function __construct($config, $logger = null)
0042     {
0043         if (is_array($config)) {
0044             $this->_config = new Zend_Config($config);
0045         } else {
0046             if ($config instanceof Zend_Config) {
0047                 $this->_config = $config;
0048             }
0049         }
0050         if (is_null($logger)) {
0051             $this->_logger = Zend_Registry::get('logger');
0052         } else {
0053             if ($logger instanceof Zend_Log) {
0054                 $this->_logger = $logger;
0055             } else {
0056                 throw new Exception('Logger must be an instance of Zend_Log');
0057             }
0058         }
0059     }
0060 
0061     /**
0062      * @param string $rawData
0063      */
0064     public function processCallback($rawData)
0065     {
0066         $this->_dataCallback = $this->_decodeData($rawData);
0067 
0068         $this->_transactionMessage = Local_Payment_Dwolla_Response::buildResponse($this->_dataCallback);
0069         $this->_transactionMessage->setMsgBody($rawData);
0070         if (APPLICATION_ENV != 'production') {
0071             $this->_logger->debug(__METHOD__ . ' - _transactionMessage - ' . print_r($this->_transactionMessage, true) . PHP_EOL);
0072         }
0073 
0074         if (false === $this->_transactionMessage->verifySignature($this->_config->consumer->access_secret)) {
0075             $this->_logger->err(__METHOD__ . ' - Abort Dwolla callback processing. Message not valid - ' . print_r($rawData, true) . PHP_EOL);
0076             return;
0077         }
0078 
0079         if (false === $this->validateTransaction()) {
0080             $this->_logger->err(__METHOD__ . ' - Abort Dwolla callback processing. Transaction not valid - ' . print_r($rawData, true) . PHP_EOL);
0081             return;
0082         }
0083 
0084         $this->_processPaymentStatus();
0085     }
0086 
0087     /**
0088      * @param $rawData
0089      * @return array
0090      */
0091     protected function _decodeData($rawData)
0092     {
0093         return json_decode($rawData, true);
0094     }
0095 
0096     protected function validateTransaction()
0097     {
0098 
0099         return $this->_checkCheckoutId() AND $this->_checkAmount();
0100 
0101     }
0102 
0103     /**
0104      * Check CheckoutId has not already been used.
0105      * Override this method to ensure CheckoutId is not a duplicate.
0106      * Throw an Exception if data is invalid or other things go wrong.
0107      */
0108     protected function _checkCheckoutId()
0109     {
0110         // check that CheckoutId has not been previously processed
0111 
0112         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0113 
0114         return false;
0115     }
0116 
0117     /**
0118      * Check that the amount is correct for item_id.
0119      * You should override this method to ensure the amount is correct.
0120      * Throw an Exception if data is invalid or other things go wrong.
0121      */
0122     protected function _checkAmount()
0123     {
0124         // check that payment_amount/payment_currency are correct
0125 
0126         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0127 
0128         return false;
0129     }
0130 
0131     protected function _processPaymentStatus()
0132     {
0133         switch ($this->_transactionMessage->getStatus()) {
0134             case 'COMPLETED':
0135                 $this->_statusCompleted();
0136                 break;
0137             case 'FAILED':
0138                 $this->_statusError();
0139                 break;
0140             case 'PENDING':
0141                 $this->_statusPending();
0142                 break;
0143             case 'PROCESSED':
0144                 $this->_statusProcessed();
0145                 break;
0146             case 'CANCELLED':
0147                 $this->_statusCancelled();
0148                 break;
0149             default:
0150                 throw new Exception('Unknown status from dwolla: ' . $this->_dataCallback['Status']);
0151         }
0152 
0153     }
0154 
0155     /**
0156      * Transaction/Payment completed.
0157      *
0158      * This is typically the most important method you'll need to override to perform
0159      * some sort of action when a successful transaction has been completed.
0160      *
0161      * You could override the other status's (such as reverse or denied) to
0162      * reverse whatever was done, but that could interfere if you're denying a
0163      * payment or refunding someone for a good reason. In those cases, it's
0164      * probably best to simply do whatever steps are required manually.
0165      */
0166     protected function _statusCompleted()
0167     {
0168         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0169     }
0170 
0171     /**
0172      * The payment failed and all attempted transfers failed or all completed transfers were successfully reversed
0173      */
0174     protected function _statusError()
0175     {
0176         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0177     }
0178 
0179     protected function _statusPending()
0180     {
0181         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0182     }
0183 
0184     protected function _statusProcessed()
0185     {
0186         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0187     }
0188 
0189     protected function _statusCancelled()
0190     {
0191         $this->_logger->err(__METHOD__ . ' - not implemented - ' . PHP_EOL);
0192     }
0193 
0194     /**
0195      * @param $proposedSignature
0196      * @param $checkoutId
0197      * @param $amount
0198      * @return bool
0199      */
0200     protected function verifyGatewaySignature($proposedSignature, $checkoutId, $amount)
0201     {
0202         $amount = number_format($amount, 2);
0203         $signature = hash_hmac("sha1", "{$checkoutId}&{$amount}", $this->_config->consumer->access_secret);
0204 
0205         return $signature == $proposedSignature;
0206     }
0207 
0208     protected function verifyWebhookSignature($body)
0209     {
0210         // Get Dwolla's signature
0211         $headers = getallheaders();
0212         $signature = $headers['X-Dwolla-Signature'];
0213 
0214         // Calculate hash, and compare to the signature
0215         $hash = hash_hmac('sha1', $body, $this->_config->consumer->access_secret);
0216 
0217         return ($hash == $signature);
0218     }
0219 
0220     private function fetchDetailedTransactionInformation()
0221     {
0222         // Create request body
0223         $requestBody = array();
0224 
0225         $response = $this->_makeRequest($requestBody, 'rest/transactions', false);
0226         if (APPLICATION_ENV != 'production') {
0227             $this->_logger->debug(__METHOD__ . ' - response - ' . print_r($response, true) . PHP_EOL);
0228         }
0229 
0230         $lastResponse = new Local_Payment_Dwolla_ResponsePayRequest($response);
0231 
0232         if (false === $lastResponse->isSuccessful()) {
0233             throw new Local_Payment_Exception('Dwolla payment request failed. Request response:' . print_r($lastResponse->getRawMessage(),
0234                     true));
0235         }
0236 
0237         return $lastResponse;
0238     }
0239 
0240     /**
0241      * @param array $request
0242      * @param string $apiNameOperation
0243      * @param bool $withAuthHeader
0244      * @throws Local_Payment_Exception
0245      * @return array
0246      */
0247     protected function _makeRequest($request, $apiNameOperation, $withAuthHeader = true)
0248     {
0249         $url = $this->_config->api->endpoint . '/' . $apiNameOperation;
0250         $http = new Zend_Http_Client($url);
0251         if (true === $withAuthHeader) {
0252             $http->setHeaders($this->_buildHeader($this->_config));
0253         }
0254         $http->setHeaders('Content-Type', 'application/json');
0255         $http->setMethod(Zend_Http_Client::POST);
0256         $http->setRawData(json_encode($request));
0257 
0258         try {
0259             $response = $http->request();
0260         } catch (Zend_Http_Client_Exception $e) {
0261             throw new Local_Payment_Exception('Error while request Dwolla website.', 0, $e);
0262         }
0263 
0264         if (false === $response) {
0265             $logMsg = __METHOD__ . "::Error while request Dwolla Website.\n Server replay was: " . $http->getLastResponse()->getStatus() . PHP_EOL . $http->getLastResponse()->getMessage() . PHP_EOL;
0266             $logMsg .= __METHOD__ . '::' . print_r($http->getLastRequest(), true) . PHP_EOL;
0267             $logMsg .= __METHOD__ . '::' . print_r($response->getHeaders(), true) . PHP_EOL;
0268             $logMsg .= __METHOD__ . '::' . print_r($response->getBody(), true) . PHP_EOL;
0269             $this->_logger->err($logMsg);
0270             throw new Local_Payment_Exception('Error while request Dwolla website.');
0271         } else {
0272             $logMsg = __METHOD__ . '::' . print_r($http->getLastRequest(), true) . PHP_EOL;
0273             $logMsg .= __METHOD__ . '::' . print_r($response->getHeaders(), true) . PHP_EOL;
0274             $logMsg .= __METHOD__ . '::' . print_r($response->getBody(), true) . PHP_EOL;
0275             $this->_logger->debug($logMsg);
0276         }
0277 
0278         return json_decode($response->getBody(), true);
0279     }
0280 
0281 }