File indexing completed on 2025-05-04 05:32:13
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_AdaptivePayment_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 /** 0038 * @param $rawData 0039 * @throws Exception 0040 */ 0041 public function processIpn($rawData) 0042 { 0043 if (false === $this->verifyIpnOrigin($rawData)) { 0044 $this->_logger->err(__FUNCTION__ . '::Abort IPN processing. IPN not verified: ' . $rawData); 0045 return; 0046 } 0047 0048 $this->_dataIpn = $this->_parseRawMessage($rawData); 0049 $this->_logger->info(__FUNCTION__ . '::_dataIpn: ' . print_r($this->_dataIpn, true) . "\n"); 0050 0051 $this->_ipnMessage = Local_Payment_PayPal_Response::buildResponse($this->_dataIpn); 0052 $this->_logger->info(__FUNCTION__ . '::_ipnMessage: ' . print_r($this->_ipnMessage, true) . "\n"); 0053 0054 if (false === $this->validateTransaction()) { 0055 $this->_logger->err(__FUNCTION__ . '::Abort IPN processing. Transaction not valid or unknown:' . $rawData); 0056 return; 0057 } 0058 0059 $this->processPaymentStatus(); 0060 0061 } 0062 0063 /** 0064 * @param string $rawDataIpn 0065 * @return bool 0066 */ 0067 public function verifyIpnOrigin($rawDataIpn) 0068 { 0069 $rawDataIpnCmd = 'cmd=_notify-validate&'; 0070 $requestParams = $rawDataIpnCmd . $rawDataIpn; 0071 0072 $url = $this->_config->ipn->endpoint . '/webscr'; 0073 0074 $response = $this->_processRequest($requestParams, $url); 0075 0076 if (strcmp($response, self::VERIFIED) == 0) { 0077 return true; 0078 } 0079 0080 return false; 0081 } 0082 0083 /** 0084 * @param string $requestData 0085 * @param string $url 0086 * @throws Local_Payment_Exception 0087 * @return string 0088 */ 0089 protected function _processRequest($requestData, $url) 0090 { 0091 $http = new Zend_Http_Client($url); 0092 $http->setMethod(Zend_Http_Client::POST); 0093 $http->setRawData($requestData); 0094 0095 try { 0096 $response = $http->request(); 0097 } catch (Zend_Http_Client_Exception $e) { 0098 throw new Local_Payment_Exception('Error while request PayPal website.', 0, $e); 0099 } 0100 0101 if (false === $response) { 0102 $this->_logger->err(__FUNCTION__ . "::Error while request PayPal Website.\n Server replay was: " . $http->getLastResponse()->getStatus() . ". " . $http->getLastResponse()->getMessage() . "\n"); 0103 $this->_logger->err(__FUNCTION__ . '::Last Request: ' . print_r($http->getLastRequest(), true)); 0104 $this->_logger->err(__FUNCTION__ . '::Headers: ' . print_r($response->getHeaders(), true)); 0105 $this->_logger->err(__FUNCTION__ . '::Body: ' . print_r($response->getBody(), true) . "\n"); 0106 } else { 0107 $this->_logger->debug(__FUNCTION__ . '::Last Request: ' . print_r($http->getLastRequest(), true)); 0108 $this->_logger->debug(__FUNCTION__ . '::Headers: ' . print_r($response->getHeaders(), true)); 0109 $this->_logger->debug(__FUNCTION__ . '::Body: ' . print_r($response->getBody(), true) . "\n"); 0110 } 0111 0112 return $response->getBody(); 0113 } 0114 0115 /** 0116 * @return bool 0117 */ 0118 protected function validateTransaction() 0119 { 0120 // Make sure the receiver email address is one of yours and the 0121 // amount of money is correct 0122 0123 return $this->_checkEmail() AND $this->_checkTxnId() AND $this->_checkAmount(); 0124 } 0125 0126 /** 0127 * Check email address for validity. 0128 * Override this method to make sure you are the one being paid. 0129 * Throw an Exception if data is invalid or other things go wrong. 0130 * 0131 * $this->_dataIpn['receiver_email'] = The email who is about to receive payment. 0132 */ 0133 protected function _checkEmail() 0134 { 0135 // check that receiver_email is your Primary PayPal email 0136 0137 $this->_logger->info('Not doing _checkEmail(' . $this->_dataIpn['receiver_email'] . ')'); 0138 0139 return false; 0140 } 0141 0142 /** 0143 * Check txnId has not already been used. 0144 * Override this method to ensure txnId is not a duplicate. 0145 * Throw an Exception if data is invalid or other things go wrong. 0146 * 0147 * $this->_dataIpn['txn_id'] = The transaction ID from paypal. 0148 */ 0149 protected function _checkTxnId() 0150 { 0151 // check that txn_id has not been previously processed 0152 0153 $this->_logger->info('Not doing _checkTxnId(' . $this->_ipnMessage->getTransactionId() . ')'); 0154 0155 return false; 0156 } 0157 0158 /** 0159 * Check that the amount/currency is correct for item_id. 0160 * You should override this method to ensure the amount is correct. 0161 * Throw an Exception if data is invalid or other things go wrong. 0162 * 0163 * $this->_dataIpn['item_number'] = The item number 0164 * $this->_dataIpn['mc_gross'] = The amount being paid 0165 * $this->_dataIpn['mc_currency'] = Currency code of amount 0166 */ 0167 protected function _checkAmount() 0168 { 0169 // check that payment_amount/payment_currency are correct 0170 0171 $this->_logger->info('Not doing _checkAmount(' . $this->_dataIpn['mc_gross'] . ', ' . $this->_dataIpn['mc_currency'] . ')'); 0172 0173 return false; 0174 } 0175 0176 protected function processPaymentStatus() 0177 { 0178 switch ($this->_dataIpn['status']) { 0179 case 'COMPLETED': 0180 $this->_statusCompleted(); 0181 break; 0182 case 'INCOMPLETE': 0183 $this->_statusIncomplete(); 0184 break; 0185 case 'CREATED': 0186 $this->_statusCreated(); 0187 break; 0188 case 'ERROR': 0189 $this->_statusError(); 0190 break; 0191 case 'REVERSALERROR': 0192 $this->_statusReversalError(); 0193 break; 0194 case 'PROCESSING': 0195 $this->_statusProcessing(); 0196 break; 0197 case 'PENDING': 0198 $this->_statusPending(); 0199 break; 0200 default: 0201 throw new Local_Payment_Exception('Unknown status from PayPal: ' . $this->_ipnMessage->getStatus()); 0202 } 0203 } 0204 0205 /** 0206 * Transaction/Payment completed. 0207 * 0208 * This is typically the most important method you'll need to override to perform 0209 * some sort of action when a successful transaction has been completed. 0210 * 0211 * You could override the other status's (such as reverse or denied) to 0212 * reverse whatever was done, but that could interfere if you're denying a 0213 * payment or refunding someone for a good reason. In those cases, it's 0214 * probably best to simply do whatever steps are required manually. 0215 */ 0216 protected function _statusCompleted() 0217 { 0218 $this->_logger->info('Not doing anything in statusCompleted ' . $this->_ipnMessage->getPaymentId()); 0219 } 0220 0221 /** 0222 * Some transfers succeeded and some failed for a parallel payment or, for a delayed chained payment, secondary receivers have not been paid 0223 */ 0224 protected function _statusIncomplete() 0225 { 0226 $this->_logger->info('Not doing anything in _statusIncomplete ' . $this->_ipnMessage->getPaymentId()); 0227 } 0228 0229 /** 0230 * The payment request was received; funds will be transferred once the payment is approved 0231 */ 0232 protected function _statusCreated() 0233 { 0234 $this->_logger->info('Not doing anything in _statusCreated ' . $this->_ipnMessage->getPaymentId()); 0235 } 0236 /** 0237 * The payment request was denied; 0238 */ 0239 protected function _statusDenied() 0240 { 0241 $this->_logger->info('Not doing anything in _statusDenied ' . $this->_ipnMessage->getPaymentId()); 0242 } 0243 /** 0244 * The payment request was reserved; 0245 */ 0246 protected function _statusReserved() 0247 { 0248 $this->_logger->info('Not doing anything in _statusReserved ' . $this->_ipnMessage->getPaymentId()); 0249 } 0250 /** 0251 * The payment request was refunded; 0252 */ 0253 protected function _statusRefunded() 0254 { 0255 $this->_logger->info('Not doing anything in _statusRefunded ' . $this->_ipnMessage->getPaymentId()); 0256 } 0257 /** 0258 * The payment request was failed; 0259 */ 0260 protected function _statusFailed() 0261 { 0262 $this->_logger->info('Not doing anything in _statusFailed ' . $this->_ipnMessage->getPaymentId()); 0263 } 0264 /** 0265 * The payment request was Partially Refunded; 0266 */ 0267 protected function _statusPartiallyRefunded() 0268 { 0269 $this->_logger->info('Not doing anything in _statusPartiallyRefunded ' . $this->_ipnMessage->getPaymentId()); 0270 } 0271 0272 0273 /** 0274 * The payment failed and all attempted transfers failed or all completed transfers were successfully reversed 0275 */ 0276 protected function _statusError() 0277 { 0278 $this->_logger->info('Not doing anything in _statusError ' . $this->_ipnMessage->getPaymentId()); 0279 } 0280 0281 /** 0282 * One or more transfers failed when attempting to reverse a payment 0283 * 0284 */ 0285 protected function _statusReversalError() 0286 { 0287 $this->_logger->info('Not doing anything in _statusReversalError ' . $this->_ipnMessage->getPaymentId() . '::' . $this->_dataIpn['reason_code']); 0288 } 0289 0290 /** 0291 * The payment is in progress 0292 */ 0293 protected function _statusProcessing() 0294 { 0295 $this->_logger->info('Not doing anything in _statusProcessing ' . $this->_ipnMessage->getPaymentId()); 0296 } 0297 0298 /** 0299 * Pending state 0300 * Look at $this->_dataIpn['pending_reason'] 0301 */ 0302 protected function _statusPending() 0303 { 0304 $this->_logger->info('Not doing anything in _statusPending: ' . $this->_ipnMessage->getPaymentId() . '::' . $this->_dataIpn['pending_reason']); 0305 } 0306 0307 public function getCharset($rawDataIpn) 0308 { 0309 $matches = array(); 0310 0311 preg_match('|charset=(.*?)\&|', $rawDataIpn, $matches); 0312 0313 return $matches[1]; 0314 } 0315 0316 protected function processTransactionStatus() 0317 { 0318 switch (strtoupper($this->_ipnMessage->getTransactionStatus())) { 0319 case 'COMPLETED': 0320 $this->_processTransactionStatusCompleted(); 0321 break; 0322 case 'PENDING': 0323 $this->_processTransactionStatusPending(); 0324 break; 0325 case 'REFUNDED': 0326 $this->_processTransactionStatusRefunded(); 0327 break; 0328 case 'DENIED': 0329 $this->_processTransactionStatusDenied(); 0330 break; 0331 default: 0332 throw new Local_Payment_Exception('Unknown transaction status from PayPal: ' . $this->_ipnMessage->getTransactionStatus()); 0333 } 0334 } 0335 0336 protected function _processTransactionStatusCompleted() 0337 { 0338 $this->_logger->info('Not doing anything in processTransactionStatusCompleted: ' . $this->_ipnMessage->getTransactionId()); 0339 } 0340 0341 protected function _processTransactionStatusPending() 0342 { 0343 $this->_logger->info('Not doing anything in processTransactionStatusPending: ' . $this->_ipnMessage->getTransactionId()); 0344 } 0345 0346 protected function _processTransactionStatusRefunded() 0347 { 0348 $this->_logger->info('Not doing anything in processTransactionStatusRefunded: ' . $this->_ipnMessage->getTransactionId()); 0349 } 0350 0351 protected function _processTransactionStatusDenied() 0352 { 0353 $this->_logger->info('Not doing anything in processTransactionStatusDenied: ' . $this->_ipnMessage->getTransactionId()); 0354 } 0355 0356 }