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_Masspay_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('Masspay ' . __FUNCTION__ . '::Abort Masspay IPN processing. IPN not verified: ' . $rawData); 0051 $this->_logger->warn('Masspay ' . __FUNCTION__ . '::Abort Masspay 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('Masspay ' . __FUNCTION__ . '::Abort 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("Masspay " . __FUNCTION__ . "Got " . curl_error($ch) . " when processing IPN data"); 0117 $this->_logger->warn("Masspay " . __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('Masspay ' . __FUNCTION__ . ' Status = ' . $this->_dataIpn['payment_status']); 0149 switch ($this->_dataIpn['payment_status']) { 0150 case 'Completed': 0151 $this->_statusCompleted(); 0152 break; 0153 case 'Denied': 0154 $this->_statusDenied(); 0155 break; 0156 case 'Processed': 0157 $this->_statusProcessed(); 0158 break; 0159 default: 0160 $this->_logger->info('Masspay ' . __FUNCTION__ . ' Status not found: ' . print_r($this->_dataIpn)); 0161 throw new Local_Payment_Exception('Unknown status from PayPal: ' . print_r($this->_dataIpn)); 0162 } 0163 $this->_logger->info('Masspay ' . __FUNCTION__ . ' Status = ' . $this->_dataIpn['payment_status'] . ' DONE'); 0164 } 0165 0166 /** 0167 * Transaction/Payment completed. 0168 * 0169 * For Mass Payments, this means that all of your payments have been claimed, 0170 * or after a period of 30 days, unclaimed payments have been returned to you. 0171 */ 0172 protected function _statusCompleted() 0173 { 0174 $this->_logger->info('Masspay _statusCompleted'); 0175 0176 /* 0177 $payer_id = $this->_dataIpn['payer_id']; 0178 $payment_date = $this->_dataIpn['payment_date']; 0179 $first_name = $this->_dataIpn['first_name']; 0180 $last_name = $this->_dataIpn['last_name']; 0181 $notify_version = $this->_dataIpn['notify_version']; 0182 $payer_status = $this->_dataIpn['payer_status']; 0183 $verify_sign = $this->_dataIpn['verify_sign']; 0184 $payer_email = $this->_dataIpn['payer_email']; 0185 $payer_business_name = $this->_dataIpn['payer_business_name']; 0186 $residence_country = $this->_dataIpn['residence_country']; 0187 $test_ipn = $this->_dataIpn['test_ipn']; 0188 $ipn_track_id = $this->_dataIpn['ipn_track_id']; 0189 */ 0190 0191 $payment_gross_x; 0192 $receiver_email_x; 0193 $mc_currency_x; 0194 $masspay_txn_id_x; 0195 $unique_id_x; 0196 $status_x; 0197 $mc_gross_x; 0198 $payment_fee_x; 0199 $mc_fee_x; 0200 0201 for ($i = 1; $i <= 250; $i++) { 0202 0203 if (isset($this->_dataIpn['payment_gross_' . $i])) { 0204 //$payment_gross_x = $this->_dataIpn['payment_gross_'.$i]; 0205 //$receiver_email_x = $this->_dataIpn['receiver_email_'.$i]; 0206 //$mc_currency_x = $this->_dataIpn['mc_currency_'.$i]; 0207 //$masspay_txn_id_x = $this->_dataIpn['masspay_txn_id_'.$i]; 0208 $unique_id_x = $this->_dataIpn['unique_id_' . $i]; 0209 $status_x = $this->_dataIpn['status_' . $i]; 0210 //$mc_gross_x = $this->_dataIpn['mc_gross_'.$i]; 0211 //$payment_fee_x = $this->_dataIpn['payment_fee_'.$i]; 0212 //$mc_fee_x = $this->_dataIpn['mc_fee_'.$i]; 0213 //save in db 0214 $payoutTable = new Default_Model_DbTable_MemberPayout(); 0215 0216 //check if old status < 100 0217 $payout = $payoutTable->fetchRow("id = " . $unique_id_x); 0218 $this->_logger->info('Masspay _statusCompleted dataset: id = ' . print_r($payout['id']) . ' - old status = ' 0219 . print_r($payout['status'])); 0220 if (isset($payout) && $payout['status'] < $payoutTable::$PAYOUT_STATUS_COMPLETED) { 0221 $payoutTable->update(array("status" => $payoutTable::$PAYOUT_STATUS_COMPLETED, 0222 "timestamp_masspay_last_ipn" => new Zend_Db_Expr('Now()'), 0223 "last_paypal_ipn" => $this->_dataRaw, 0224 "last_paypal_status" => $status_x 0225 ), "id = " . $unique_id_x); 0226 } 0227 } else { 0228 break; 0229 } 0230 } 0231 } 0232 0233 /** 0234 * For Mass Payments, this means that your funds were not sent and the Mass Payment was not initiated. 0235 * This may have been caused by lack of funds. 0236 */ 0237 protected function _statusDenied() 0238 { 0239 $this->_logger->info('Masspay _statusDenied'); 0240 0241 /* 0242 $payer_id = $this->_dataIpn['payer_id']; 0243 $payment_date = $this->_dataIpn['payment_date']; 0244 $first_name = $this->_dataIpn['first_name']; 0245 $last_name = $this->_dataIpn['last_name']; 0246 $notify_version = $this->_dataIpn['notify_version']; 0247 $payer_status = $this->_dataIpn['payer_status']; 0248 $verify_sign = $this->_dataIpn['verify_sign']; 0249 $payer_email = $this->_dataIpn['payer_email']; 0250 $payer_business_name = $this->_dataIpn['payer_business_name']; 0251 $residence_country = $this->_dataIpn['residence_country']; 0252 $test_ipn = $this->_dataIpn['test_ipn']; 0253 $ipn_track_id = $this->_dataIpn['ipn_track_id']; 0254 */ 0255 $payment_gross_x; 0256 $receiver_email_x; 0257 $mc_currency_x; 0258 $masspay_txn_id_x; 0259 $unique_id_x; 0260 $status_x; 0261 $mc_gross_x; 0262 $payment_fee_x; 0263 $mc_fee_x; 0264 0265 for ($i = 1; $i <= 250; $i++) { 0266 if (isset($this->_dataIpn['payment_gross_' . $i])) { 0267 //$payment_gross_x = $this->_dataIpn['payment_gross_'.$i]; 0268 //$receiver_email_x = $this->_dataIpn['receiver_email_'.$i]; 0269 //$mc_currency_x = $this->_dataIpn['mc_currency_'.$i]; 0270 //$masspay_txn_id_x = $this->_dataIpn['masspay_txn_id_'.$i]; 0271 $unique_id_x = $this->_dataIpn['unique_id_' . $i]; 0272 $status_x = $this->_dataIpn['status_' . $i]; 0273 //$mc_gross_x = $this->_dataIpn['mc_gross_'.$i]; 0274 //$payment_fee_x = $this->_dataIpn['payment_fee_'.$i]; 0275 //$mc_fee_x = $this->_dataIpn['mc_fee_'.$i]; 0276 //save in db 0277 $payoutTable = new Default_Model_DbTable_MemberPayout(); 0278 $payout = $payoutTable->fetchRow("id = " . $unique_id_x); 0279 $this->_logger->info('Masspay _statusDenied old dataset: ' . print_r($payout['status'])); 0280 $payoutTable->update(array("status" => $payoutTable::$PAYOUT_STATUS_DENIED, 0281 "timestamp_masspay_last_ipn" => new Zend_Db_Expr('Now()'), 0282 "last_paypal_ipn" => $this->_dataRaw, 0283 "last_paypal_status" => $status_x 0284 ), "id = " . $unique_id_x); 0285 } else { 0286 break; 0287 } 0288 } 0289 } 0290 0291 /** 0292 * Your Mass Payment has been processed and all payments have been sent. 0293 */ 0294 protected function _statusProcessed() 0295 { 0296 $this->_logger->info('Masspay _statusProcessed'); 0297 0298 /* 0299 $payer_id = $this->_dataIpn['payer_id']; 0300 $payment_date = $this->_dataIpn['payment_date']; 0301 $first_name = $this->_dataIpn['first_name']; 0302 $last_name = $this->_dataIpn['last_name']; 0303 $notify_version = $this->_dataIpn['notify_version']; 0304 $payer_status = $this->_dataIpn['payer_status']; 0305 $verify_sign = $this->_dataIpn['verify_sign']; 0306 $payer_email = $this->_dataIpn['payer_email']; 0307 $payer_business_name = $this->_dataIpn['payer_business_name']; 0308 $residence_country = $this->_dataIpn['residence_country']; 0309 $test_ipn = $this->_dataIpn['test_ipn']; 0310 $ipn_track_id = $this->_dataIpn['ipn_track_id']; 0311 */ 0312 $payment_gross_x; 0313 $receiver_email_x; 0314 $mc_currency_x; 0315 $masspay_txn_id_x; 0316 $unique_id_x; 0317 $status_x; 0318 $mc_gross_x; 0319 $payment_fee_x; 0320 $mc_fee_x; 0321 0322 for ($i = 1; $i <= 250; $i++) { 0323 if (isset($this->_dataIpn['payment_gross_' . $i])) { 0324 //$payment_gross_x = $this->_dataIpn['payment_gross_'.$i]; 0325 //$receiver_email_x = $this->_dataIpn['receiver_email_'.$i]; 0326 //$mc_currency_x = $this->_dataIpn['mc_currency_'.$i]; 0327 //$masspay_txn_id_x = $this->_dataIpn['masspay_txn_id_'.$i]; 0328 $unique_id_x = $this->_dataIpn['unique_id_' . $i]; 0329 $status_x = $this->_dataIpn['status_' . $i]; 0330 //$mc_gross_x = $this->_dataIpn['mc_gross_'.$i]; 0331 //$payment_fee_x = $this->_dataIpn['payment_fee_'.$i]; 0332 //$mc_fee_x = $this->_dataIpn['mc_fee_'.$i]; 0333 //save in db 0334 $payoutTable = new Default_Model_DbTable_MemberPayout(); 0335 //check if old status < 100 0336 $payout = $payoutTable->fetchRow("id = " . $unique_id_x); 0337 $this->_logger->info('Masspay _statusProcessed dataset: id = ' . print_r($payout['id']) . ' - old status = ' 0338 . print_r($payout['status'])); 0339 0340 if ($payout && $payout['status'] < $payoutTable::$PAYOUT_STATUS_PROCESSED) { 0341 $payoutTable->update(array("status" => $payoutTable::$PAYOUT_STATUS_PROCESSED, 0342 "timestamp_masspay_last_ipn" => new Zend_Db_Expr('Now()'), 0343 "last_paypal_ipn" => $this->_dataRaw, 0344 "last_paypal_status" => $status_x 0345 ), "id = " . $unique_id_x); 0346 } 0347 } else { 0348 break; 0349 } 0350 } 0351 } 0352 0353 /** 0354 * @param $rawDataIpn 0355 * 0356 * @return mixed 0357 */ 0358 public function getCharset($rawDataIpn) 0359 { 0360 $matches = array(); 0361 0362 preg_match('|charset=(.*?)\&|', $rawDataIpn, $matches); 0363 0364 return $matches[1]; 0365 } 0366 0367 /** 0368 * @param string $requestData 0369 * @param string $url 0370 * 0371 * @return string 0372 * @throws Local_Payment_Exception 0373 * @throws Zend_Http_Client_Exception 0374 */ 0375 protected function _makeRequest($requestData, $url) 0376 { 0377 $http = new Zend_Http_Client($url); 0378 $http->setMethod(Zend_Http_Client::POST); 0379 $http->setRawData($requestData); 0380 0381 try { 0382 $response = $http->request(); 0383 } catch (Zend_Http_Client_Exception $e) { 0384 throw new Local_Payment_Exception('Error while request PayPal website.', 0, $e); 0385 } 0386 0387 if (false === $response) { 0388 $this->_logger->err(__FUNCTION__ . "::Error while request PayPal Website.\n Server replay was: " . $http->getLastResponse() 0389 ->getStatus() 0390 . ". " . $http->getLastResponse()->getMessage() . "\n"); 0391 $this->_logger->err(__FUNCTION__ . '::Last Request: ' . print_r($http->getLastRequest(), true)); 0392 $this->_logger->err(__FUNCTION__ . '::Headers: ' . print_r($response->getHeaders(), true)); 0393 $this->_logger->err(__FUNCTION__ . '::Body: ' . print_r($response->getBody(), true) . "\n"); 0394 } else { 0395 $this->_logger->debug(__FUNCTION__ . '::Last Request: ' . print_r($http->getLastRequest(), true)); 0396 $this->_logger->debug(__FUNCTION__ . '::Headers: ' . print_r($response->getHeaders(), true)); 0397 $this->_logger->debug(__FUNCTION__ . '::Body: ' . print_r($response->getBody(), true) . "\n"); 0398 } 0399 0400 return $response->getBody(); 0401 } 0402 0403 /** 0404 * Check email address for validity. 0405 * Override this method to make sure you are the one being paid. 0406 * Throw an Exception if data is invalid or other things go wrong. 0407 * 0408 * $this->_dataIpn['receiver_email'] = The email who is about to receive payment. 0409 */ 0410 protected function _checkEmail() 0411 { 0412 // check that receiver_email is your Primary PayPal email 0413 0414 $this->_logger->info('Not doing _checkEmail(' . $this->_dataIpn['receiver_email'] . ')'); 0415 0416 return false; 0417 } 0418 0419 /** 0420 * Check txnId has not already been used. 0421 * Override this method to ensure txnId is not a duplicate. 0422 * Throw an Exception if data is invalid or other things go wrong. 0423 * 0424 * $this->_dataIpn['txn_id'] = The transaction ID from paypal. 0425 */ 0426 protected function _checkTxnId() 0427 { 0428 // check that txn_id has not been previously processed 0429 0430 $this->_logger->info('Not doing _checkTxnId(' . $this->_ipnMessage->getTransactionId() . ')'); 0431 0432 return false; 0433 } 0434 0435 /** 0436 * Check that the amount/currency is correct for item_id. 0437 * You should override this method to ensure the amount is correct. 0438 * Throw an Exception if data is invalid or other things go wrong. 0439 * 0440 * $this->_dataIpn['item_number'] = The item number 0441 * $this->_dataIpn['mc_gross'] = The amount being paid 0442 * $this->_dataIpn['mc_currency'] = Currency code of amount 0443 */ 0444 protected function _checkAmount() 0445 { 0446 // check that payment_amount/payment_currency are correct 0447 0448 $this->_logger->info('Not doing _checkAmount(' . $this->_dataIpn['mc_gross'] . ', ' . $this->_dataIpn['mc_currency'] . ')'); 0449 0450 return false; 0451 } 0452 0453 }