File indexing completed on 2025-05-04 05:32:14

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_PayPal_Support_Ipn extends Local_Payment_PayPal_Base
0024 {
0025 
0026     const VERIFIED = 'VERIFIED';
0027 
0028     /** @var \Zend_Config */
0029     protected $_config;
0030     /** @var \Zend_Log */
0031     protected $_logger;
0032     /** @var  array */
0033     protected $_dataIpn;
0034     /** @var  \Local_Payment_PayPal_PaymentInterface */
0035     protected $_ipnMessage;
0036 
0037     protected $_dataRaw;
0038 
0039     /**
0040      * @param $rawData
0041      *
0042      * @throws Exception
0043      */
0044     public function processIpn($rawData)
0045     {
0046         $this->_config = Zend_Registry::get('config');
0047         $this->_logger = Zend_Registry::get('logger');
0048 
0049         if (false === $this->verifyIpnOrigin($rawData)) {
0050             $this->_logger->err(' ' . __FUNCTION__ . '::Abort Support IPN processing. IPN not verified: ' . $rawData);
0051             $this->_logger->info(' ' . __FUNCTION__ . '::Abort Support IPN processing. IPN not verified: ' . $rawData);
0052 
0053             return;
0054         }
0055 
0056         $this->_dataRaw = $rawData;
0057         $this->_dataIpn = $this->_parseRawMessage($rawData);
0058         $this->_ipnMessage = Local_Payment_PayPal_Response::buildResponse($this->_dataIpn);
0059 
0060         if (false === $this->validateTransaction()) {
0061             $this->_logger->err(' ' . __FUNCTION__ . '::Abort Support IPN processing. Transaction not valid:' . $rawData);
0062 
0063             return;
0064         }
0065 
0066         $this->processPaymentStatus();
0067     }
0068 
0069     /**
0070      * @param string $rawDataIpn
0071      *
0072      * @return bool
0073      */
0074     public function verifyIpnOrigin($rawDataIpn)
0075     {
0076         $raw_post_array = explode('&', $rawDataIpn);
0077         $myPost = array();
0078         foreach ($raw_post_array as $keyval) {
0079             $keyval = explode('=', $keyval);
0080             if (count($keyval) == 2) {
0081                 $myPost[$keyval[0]] = urldecode($keyval[1]);
0082             }
0083         }
0084         // read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
0085         $req = 'cmd=_notify-validate';
0086         if (function_exists('get_magic_quotes_gpc')) {
0087             $get_magic_quotes_exists = true;
0088         }
0089         foreach ($myPost as $key => $value) {
0090             if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
0091                 $value = urlencode(stripslashes($value));
0092             } else {
0093                 $value = urlencode($value);
0094             }
0095             $req .= "&$key=$value";
0096         }
0097 
0098         // Step 2: POST IPN data back to PayPal to validate
0099         $url = $this->_config->third_party->paypal->masspay->ipn->endpoint . "/webscr";
0100         //$ch = curl_init('https://ipnpb.paypal.com/cgi-bin/webscr');
0101         $ch = curl_init($url);
0102         curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
0103         curl_setopt($ch, CURLOPT_POST, 1);
0104         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
0105         curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
0106         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
0107         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
0108         curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
0109         curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
0110         // In wamp-like environments that do not come bundled with root authority certificates,
0111         // please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set
0112         // the directory path of the certificate as shown below:
0113         // curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
0114         if (!($res = curl_exec($ch))) {
0115             // error_log("Got " . curl_error($ch) . " when processing IPN data");
0116             $this->_logger->err(" " . __FUNCTION__ . "Got " . curl_error($ch) . " when processing IPN data");
0117             $this->_logger->info(" " . __FUNCTION__ . "Got " . curl_error($ch) . " when processing IPN data");
0118             curl_close($ch);
0119             exit;
0120         }
0121         curl_close($ch);
0122 
0123         if (strcmp($res, "VERIFIED") == 0) {
0124             // The IPN is verified, process it
0125             return true;
0126         } else if (strcmp($res, "INVALID") == 0) {
0127             // IPN invalid, log for manual investigation
0128             return false;
0129         }
0130 
0131         return false;
0132     }
0133 
0134     /**
0135      * @return bool
0136      */
0137     protected function validateTransaction()
0138     {
0139         // Make sure the receiver email address is one of yours and the
0140         // amount of money is correct
0141 
0142         //return $this->_checkEmail() AND $this->_checkTxnId() AND $this->_checkAmount();
0143         return true;
0144     }
0145 
0146     protected function processPaymentStatus()
0147     {
0148         $this->_logger->info(' ' . __FUNCTION__ . ' IPN: ' . print_r($this->_ipnMessage, true) . ' Status: '
0149             . $this->_ipnMessage->getStatus());
0150         switch ($this->_ipnMessage->getStatus()) {
0151             case 'COMPLETED':
0152                 $this->_statusCompleted();
0153                 break;
0154             /*case 'Denied':
0155                 $this->_statusDenied();
0156                 break;
0157             case 'Processed':
0158                 $this->_statusProcessed();
0159                 break;*/
0160             default:
0161                 $this->_logger->info(' ' . __FUNCTION__ . ' Status not found: . IPN: ' . print_r($this->_ipnMessage, true)
0162                     . ' Status: ' . print_r($this->_ipnMessage->getStatus(), true));
0163                 throw new Local_Payment_Exception('Unknown status from PayPal: ' . print_r($this->_ipnMessage));
0164         }
0165         $this->_logger->info(' ' . __FUNCTION__ . ' Status = ' . $this->_ipnMessage->getStatus() . ' DONE');
0166     }
0167 
0168     /**
0169      * Transaction/Payment completed.
0170      *
0171      * For Mass Payments, this means that all of your payments have been claimed,
0172      * or after a period of 30 days, unclaimed payments have been returned to you.
0173      */
0174     protected function _statusCompleted()
0175     {
0176         $this->_logger->info(' ' . __FUNCTION__ . ' set Status');
0177 
0178         $Table = new Default_Model_DbTable_Support();
0179         $Table->activateSupportFromResponse($this->_ipnMessage);
0180     }
0181 
0182     public function getCharset($rawDataIpn)
0183     {
0184         $matches = array();
0185 
0186         preg_match('|charset=(.*?)\&|', $rawDataIpn, $matches);
0187 
0188         return $matches[1];
0189     }
0190 
0191     /**
0192      * @param string $requestData
0193      * @param string $url
0194      *
0195      * @return string
0196      * @throws Local_Payment_Exception
0197      * @throws Zend_Http_Client_Exception
0198      */
0199     protected function _makeRequest($requestData, $url)
0200     {
0201         $http = new Zend_Http_Client($url);
0202         $http->setMethod(Zend_Http_Client::POST);
0203         $http->setRawData($requestData);
0204 
0205         try {
0206             $response = $http->request();
0207         } catch (Zend_Http_Client_Exception $e) {
0208             throw new Local_Payment_Exception('Error while request PayPal website.', 0, $e);
0209         }
0210 
0211         if (false === $response) {
0212             $this->_logger->err(__FUNCTION__ . "::Error while request PayPal Website.\n Server replay was: " . $http->getLastResponse()
0213                                                                                                                     ->getStatus()
0214                 . ". " . $http->getLastResponse()->getMessage() . "\n");
0215             $this->_logger->err(__FUNCTION__ . '::Last Request: ' . print_r($http->getLastRequest(), true));
0216             $this->_logger->err(__FUNCTION__ . '::Headers: ' . print_r($response->getHeaders(), true));
0217             $this->_logger->err(__FUNCTION__ . '::Body: ' . print_r($response->getBody(), true) . "\n");
0218         } else {
0219             $this->_logger->debug(__FUNCTION__ . '::Last Request: ' . print_r($http->getLastRequest(), true));
0220             $this->_logger->debug(__FUNCTION__ . '::Headers: ' . print_r($response->getHeaders(), true));
0221             $this->_logger->debug(__FUNCTION__ . '::Body: ' . print_r($response->getBody(), true) . "\n");
0222         }
0223 
0224         return $response->getBody();
0225     }
0226 
0227     /**
0228      * Check email address for validity.
0229      * Override this method to make sure you are the one being paid.
0230      * Throw an Exception if data is invalid or other things go wrong.
0231      *
0232      * $this->_dataIpn['receiver_email'] = The email who is about to receive payment.
0233      */
0234     protected function _checkEmail()
0235     {
0236         // check that receiver_email is your Primary PayPal email
0237 
0238         $this->_logger->info('Not doing _checkEmail(' . $this->_dataIpn['receiver_email'] . ')');
0239 
0240         return false;
0241     }
0242 
0243     /**
0244      * Check txnId has not already been used.
0245      * Override this method to ensure txnId is not a duplicate.
0246      * Throw an Exception if data is invalid or other things go wrong.
0247      *
0248      * $this->_dataIpn['txn_id'] = The transaction ID from paypal.
0249      */
0250     protected function _checkTxnId()
0251     {
0252         // check that txn_id has not been previously processed
0253 
0254         $this->_logger->info('Not doing _checkTxnId(' . $this->_ipnMessage->getTransactionId() . ')');
0255 
0256         return false;
0257     }
0258 
0259     /**
0260      * Check that the amount/currency is correct for item_id.
0261      * You should override this method to ensure the amount is correct.
0262      * Throw an Exception if data is invalid or other things go wrong.
0263      *
0264      * $this->_dataIpn['item_number'] = The item number
0265      * $this->_dataIpn['mc_gross']    = The amount being paid
0266      * $this->_dataIpn['mc_currency'] = Currency code of amount
0267      */
0268     protected function _checkAmount()
0269     {
0270         // check that payment_amount/payment_currency are correct
0271 
0272         $this->_logger->info('Not doing _checkAmount(' . $this->_dataIpn['mc_gross'] . ', ' . $this->_dataIpn['mc_currency'] . ')');
0273 
0274         return false;
0275     }
0276 
0277     /**
0278      * For Mass Payments, this means that your funds were not sent and the Mass Payment was not initiated.
0279      * This may have been caused by lack of funds.
0280      */
0281     protected function _statusDenied()
0282     {
0283         $this->_logger->info(' _statusDenied');
0284 
0285         $Table = new Default_Model_DbTable_Support();
0286         $Table->deactivateSupportFromResponse($this->_ipnMessage);
0287     }
0288 
0289     /**
0290      * Your Mass Payment has been processed and all payments have been sent.
0291      */
0292     protected function _statusProcessed()
0293     {
0294         $this->_logger->info(' _statusProcessed');
0295     }
0296 
0297 
0298 }