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 }