Overview

Packages

  • currencysymbol
  • MAbout
  • Mage
    • Admin
    • Adminhtml
    • AdminNotification
    • Api
    • Api2
    • Authorizenet
    • Backup
    • Bundle
    • Captcha
    • Catalog
    • CatalogIndex
    • CatalogInventory
    • CatalogRule
    • CatalogSearch
    • Centinel
    • Checkout
    • Cms
    • Compiler
    • Connect
    • Contacts
    • Core
    • Cron
    • CurrencySymbol
    • Customer
    • Dataflow
    • Directory
    • DirtectPost
    • Downloadable
    • Eav
    • GiftMessage
    • GoogleAnalytics
    • GoogleBase
    • GoogleCheckout
    • ImportExport
    • Index
    • Install
    • Log
    • Media
    • Newsletter
    • Oauth
    • Page
    • PageCache
    • Paygate
    • Payment
    • Paypal
    • PaypalUk
    • Persistent
    • Poll
    • ProductAlert
    • Rating
    • Reports
    • Review
    • Rss
    • Rule
    • Sales
    • SalesRule
    • Sedfriend
    • Sendfriend
    • Shipping
    • Sitemap
    • Tag
    • Tax
    • Usa
    • Weee
    • Widget
    • Wishlist
    • XmlConnect
  • None
  • Phoenix
    • Moneybookers
  • PHP
  • Zend
    • Date
    • Mime
    • XmlRpc

Classes

  • Mage_Paypal_Adminhtml_Paypal_ReportsController
  • Mage_Paypal_Block_Adminhtml_Settlement_Details
  • Mage_Paypal_Block_Adminhtml_Settlement_Details_Form
  • Mage_Paypal_Block_Adminhtml_Settlement_Report
  • Mage_Paypal_Block_Adminhtml_Settlement_Report_Grid
  • Mage_Paypal_Block_Adminhtml_System_Config_ApiWizard
  • Mage_Paypal_Block_Adminhtml_System_Config_Field_Country
  • Mage_Paypal_Block_Adminhtml_System_Config_Field_Hidden
  • Mage_Paypal_Block_Adminhtml_System_Config_Fieldset_Expanded
  • Mage_Paypal_Block_Adminhtml_System_Config_Fieldset_Group
  • Mage_Paypal_Block_Adminhtml_System_Config_Fieldset_Hint
  • Mage_Paypal_Block_Adminhtml_System_Config_Fieldset_Location
  • Mage_Paypal_Block_Adminhtml_System_Config_Fieldset_Payment
  • Mage_Paypal_Block_Adminhtml_System_Config_Fieldset_Store
  • Mage_Paypal_Block_Adminhtml_System_Config_Payflowlink_Advanced
  • Mage_Paypal_Block_Adminhtml_System_Config_Payflowlink_Info
  • Mage_Paypal_Block_Express_Form
  • Mage_Paypal_Block_Express_Review
  • Mage_Paypal_Block_Express_Review_Billing
  • Mage_Paypal_Block_Express_Review_Details
  • Mage_Paypal_Block_Express_Review_Shipping
  • Mage_Paypal_Block_Express_Shortcut
  • Mage_Paypal_Block_Hosted_Pro_Form
  • Mage_Paypal_Block_Hosted_Pro_Iframe
  • Mage_Paypal_Block_Hosted_Pro_Info
  • Mage_Paypal_Block_Iframe
  • Mage_Paypal_Block_Logo
  • Mage_Paypal_Block_Payflow_Advanced_Form
  • Mage_Paypal_Block_Payflow_Advanced_Iframe
  • Mage_Paypal_Block_Payflow_Advanced_Info
  • Mage_Paypal_Block_Payflow_Link_Form
  • Mage_Paypal_Block_Payflow_Link_Iframe
  • Mage_Paypal_Block_Payflow_Link_Info
  • Mage_Paypal_Block_Payment_Info
  • Mage_Paypal_Block_Standard_Form
  • Mage_Paypal_Block_Standard_Redirect
  • Mage_Paypal_Controller_Express_Abstract
  • Mage_Paypal_ExpressController
  • Mage_Paypal_Helper_Data
  • Mage_Paypal_Helper_Hss
  • Mage_Paypal_HostedproController
  • Mage_Paypal_IpnController
  • Mage_Paypal_Model_Api_Abstract
  • Mage_Paypal_Model_Api_Nvp
  • Mage_Paypal_Model_Api_Standard
  • Mage_Paypal_Model_Cart
  • Mage_Paypal_Model_Cert
  • Mage_Paypal_Model_Config
  • Mage_Paypal_Model_Direct
  • Mage_Paypal_Model_Express
  • Mage_Paypal_Model_Express_Checkout
  • Mage_Paypal_Model_Hostedpro
  • Mage_Paypal_Model_Hostedpro_Request
  • Mage_Paypal_Model_Info
  • Mage_Paypal_Model_Ipn
  • Mage_Paypal_Model_Method_Agreement
  • Mage_Paypal_Model_Mysql4_Cert
  • Mage_Paypal_Model_Mysql4_Report_Settlement
  • Mage_Paypal_Model_Mysql4_Report_Settlement_Row
  • Mage_Paypal_Model_Mysql4_Report_Settlement_Row_Collection
  • Mage_Paypal_Model_Mysql4_Setup
  • Mage_Paypal_Model_Observer
  • Mage_Paypal_Model_Payflow_Request
  • Mage_Paypal_Model_Payflowadvanced
  • Mage_Paypal_Model_Payflowlink
  • Mage_Paypal_Model_Payflowpro
  • Mage_Paypal_Model_Payment_Transaction
  • Mage_Paypal_Model_Pro
  • Mage_Paypal_Model_Report_Settlement
  • Mage_Paypal_Model_Report_Settlement_Row
  • Mage_Paypal_Model_Resource_Cert
  • Mage_Paypal_Model_Resource_Report_Settlement
  • Mage_Paypal_Model_Resource_Report_Settlement_Row
  • Mage_Paypal_Model_Resource_Report_Settlement_Row_Collection
  • Mage_Paypal_Model_Resource_Setup
  • Mage_Paypal_Model_Session
  • Mage_Paypal_Model_Standard
  • Mage_Paypal_Model_System_Config_Backend_Cert
  • Mage_Paypal_Model_System_Config_Backend_Cron
  • Mage_Paypal_Model_System_Config_Backend_MerchantCountry
  • Mage_Paypal_Model_System_Config_Source_BuyerCountry
  • Mage_Paypal_Model_System_Config_Source_FetchingSchedule
  • Mage_Paypal_Model_System_Config_Source_Logo
  • Mage_Paypal_Model_System_Config_Source_MerchantCountry
  • Mage_Paypal_Model_System_Config_Source_PaymentActions
  • Mage_Paypal_Model_System_Config_Source_PaymentActions_Express
  • Mage_Paypal_Model_System_Config_Source_RequireBillingAddress
  • Mage_Paypal_Model_System_Config_Source_UrlMethod
  • Mage_Paypal_PayflowadvancedController
  • Mage_Paypal_PayflowController
  • Mage_Paypal_StandardController

Exceptions

  • Mage_Paypal_Exception
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Magento
  4:  *
  5:  * NOTICE OF LICENSE
  6:  *
  7:  * This source file is subject to the Open Software License (OSL 3.0)
  8:  * that is bundled with this package in the file LICENSE.txt.
  9:  * It is also available through the world-wide-web at this URL:
 10:  * http://opensource.org/licenses/osl-3.0.php
 11:  * If you did not receive a copy of the license and are unable to
 12:  * obtain it through the world-wide-web, please send an email
 13:  * to license@magentocommerce.com so we can send you a copy immediately.
 14:  *
 15:  * DISCLAIMER
 16:  *
 17:  * Do not edit or add to this file if you wish to upgrade Magento to newer
 18:  * versions in the future. If you wish to customize Magento for your
 19:  * needs please refer to http://www.magentocommerce.com for more information.
 20:  *
 21:  * @category    Mage
 22:  * @package     Mage_Paypal
 23:  * @copyright   Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
 24:  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 25:  */
 26: 
 27: /**
 28:  * PayPal Instant Payment Notification processor model
 29:  */
 30: class Mage_Paypal_Model_Ipn
 31: {
 32:     /**
 33:      * Default log filename
 34:      *
 35:      * @var string
 36:      */
 37:     const DEFAULT_LOG_FILE = 'paypal_unknown_ipn.log';
 38: 
 39:     /*
 40:      * @param Mage_Sales_Model_Order
 41:      */
 42:     protected $_order = null;
 43: 
 44:     /*
 45:      * Recurring profile instance
 46:      *
 47:      * @var Mage_Sales_Model_Recurring_Profile
 48:      */
 49:     protected $_recurringProfile = null;
 50: 
 51:     /**
 52:      *
 53:      * @var Mage_Paypal_Model_Config
 54:      */
 55:     protected $_config = null;
 56: 
 57:     /**
 58:      * PayPal info instance
 59:      *
 60:      * @var Mage_Paypal_Model_Info
 61:      */
 62:     protected $_info = null;
 63: 
 64:     /**
 65:      * IPN request data
 66:      * @var array
 67:      */
 68:     protected $_request = array();
 69: 
 70:     /**
 71:      * Collected debug information
 72:      *
 73:      * @var array
 74:      */
 75:     protected $_debugData = array();
 76: 
 77:     /**
 78:      * IPN request data getter
 79:      *
 80:      * @param string $key
 81:      * @return array|string
 82:      */
 83:     public function getRequestData($key = null)
 84:     {
 85:         if (null === $key) {
 86:             return $this->_request;
 87:         }
 88:         return isset($this->_request[$key]) ? $this->_request[$key] : null;
 89:     }
 90: 
 91:     /**
 92:      * Get ipn data, send verification to PayPal, run corresponding handler
 93:      *
 94:      * @param array $request
 95:      * @param Zend_Http_Client_Adapter_Interface $httpAdapter
 96:      * @throws Exception
 97:      */
 98:     public function processIpnRequest(array $request, Zend_Http_Client_Adapter_Interface $httpAdapter = null)
 99:     {
100:         $this->_request   = $request;
101:         $this->_debugData = array('ipn' => $request);
102:         ksort($this->_debugData['ipn']);
103: 
104:         try {
105:             if (isset($this->_request['txn_type']) && 'recurring_payment' == $this->_request['txn_type']) {
106:                 $this->_getRecurringProfile();
107:                 if ($httpAdapter) {
108:                     $this->_postBack($httpAdapter);
109:                 }
110:                 $this->_processRecurringProfile();
111:             } else {
112:                 $this->_getOrder();
113:                 if ($httpAdapter) {
114:                     $this->_postBack($httpAdapter);
115:                 }
116:                 $this->_processOrder();
117:             }
118:         } catch (Exception $e) {
119:             $this->_debugData['exception'] = $e->getMessage();
120:             $this->_debug();
121:             throw $e;
122:         }
123:         $this->_debug();
124:     }
125: 
126:     /**
127:      * Post back to PayPal to check whether this request is a valid one
128:      *
129:      * @param Zend_Http_Client_Adapter_Interface $httpAdapter
130:      */
131:     protected function _postBack(Zend_Http_Client_Adapter_Interface $httpAdapter)
132:     {
133:             $sReq = '';
134:             foreach ($this->_request as $k => $v) {
135:                 $sReq .= '&'.$k.'='.urlencode($v);
136:             }
137:             $sReq .= "&cmd=_notify-validate";
138:             $sReq = substr($sReq, 1);
139:             $this->_debugData['postback'] = $sReq;
140:             $this->_debugData['postback_to'] = $this->_config->getPaypalUrl();
141: 
142:             $httpAdapter->setConfig(array('verifypeer' => $this->_config->verifyPeer));
143:             $httpAdapter->write(Zend_Http_Client::POST, $this->_config->getPaypalUrl(), '1.1', array(), $sReq);
144:             try {
145:                 $response = $httpAdapter->read();
146:             } catch (Exception $e) {
147:                 $this->_debugData['http_error'] = array('error' => $e->getMessage(), 'code' => $e->getCode());
148:                 throw $e;
149:             }
150:             $this->_debugData['postback_result'] = $response;
151: 
152:             $response = preg_split('/^\r?$/m', $response, 2);
153:             $response = trim($response[1]);
154:             if ($response != 'VERIFIED') {
155:                 throw new Exception('PayPal IPN postback failure. See ' . self::DEFAULT_LOG_FILE . ' for details.');
156:             }
157:             unset($this->_debugData['postback'], $this->_debugData['postback_result']);
158:     }
159: 
160:     /**
161:      * Load and validate order, instantiate proper configuration
162:      *
163:      *
164:      * @return Mage_Sales_Model_Order
165:      * @throws Exception
166:      */
167:     protected function _getOrder()
168:     {
169:         if (empty($this->_order)) {
170:             // get proper order
171:             $id = $this->_request['invoice'];
172:             $this->_order = Mage::getModel('sales/order')->loadByIncrementId($id);
173:             if (!$this->_order->getId()) {
174:                 $this->_debugData['exception'] = sprintf('Wrong order ID: "%s".', $id);
175:                 $this->_debug();
176:                 Mage::app()->getResponse()
177:                     ->setHeader('HTTP/1.1','503 Service Unavailable')
178:                     ->sendResponse();
179:                 exit;
180:             }
181:             // re-initialize config with the method code and store id
182:             $methodCode = $this->_order->getPayment()->getMethod();
183:             $this->_config = Mage::getModel('paypal/config', array($methodCode, $this->_order->getStoreId()));
184:             if (!$this->_config->isMethodActive($methodCode) || !$this->_config->isMethodAvailable()) {
185:                 throw new Exception(sprintf('Method "%s" is not available.', $methodCode));
186:             }
187: 
188:             $this->_verifyOrder();
189:         }
190:         return $this->_order;
191:     }
192: 
193:     /**
194:      * Load recurring profile
195:      *
196:      * @return Mage_Sales_Model_Recurring_Profile
197:      * @throws Exception
198:      */
199:     protected function _getRecurringProfile()
200:     {
201:         if (empty($this->_recurringProfile)) {
202:             // get proper recurring profile
203:             $internalReferenceId = $this->_request['rp_invoice_id'];
204:             $this->_recurringProfile = Mage::getModel('sales/recurring_profile')
205:                 ->loadByInternalReferenceId($internalReferenceId);
206:             if (!$this->_recurringProfile->getId()) {
207:                 throw new Exception(
208:                     sprintf('Wrong recurring profile INTERNAL_REFERENCE_ID: "%s".', $internalReferenceId)
209:                 );
210:             }
211:             // re-initialize config with the method code and store id
212:             $methodCode = $this->_recurringProfile->getMethodCode();
213:             $this->_config = Mage::getModel(
214:                 'paypal/config', array($methodCode, $this->_recurringProfile->getStoreId())
215:             );
216:             if (!$this->_config->isMethodActive($methodCode) || !$this->_config->isMethodAvailable()) {
217:                 throw new Exception(sprintf('Method "%s" is not available.', $methodCode));
218:             }
219:         }
220:         return $this->_recurringProfile;
221:     }
222: 
223:     /**
224:      * Validate incoming request data, as PayPal recommends
225:      *
226:      * @throws Exception
227:      * @link https://cms.paypal.com/cgi-bin/marketingweb?cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro
228:      */
229:     protected function _verifyOrder()
230:     {
231:         // verify merchant email intended to receive notification
232:         $merchantEmail = $this->_config->businessAccount;
233:         if ($merchantEmail) {
234:             $receiverEmail = $this->getRequestData('business');
235:             if (!$receiverEmail) {
236:                 $receiverEmail = $this->getRequestData('receiver_email');
237:             }
238:             if (strtolower($merchantEmail) != strtolower($receiverEmail)) {
239:                 throw new Exception(
240:                     sprintf(
241:                         'Requested %s and configured %s merchant emails do not match.', $receiverEmail, $merchantEmail
242:                     )
243:                 );
244:             }
245:         }
246:     }
247: 
248:     /**
249:      * IPN workflow implementation
250:      * Everything should be added to order comments. In positive processing cases customer will get email notifications.
251:      * Admin will be notified on errors.
252:      */
253:     protected function _processOrder()
254:     {
255:         $this->_order = null;
256:         $this->_getOrder();
257: 
258:         $this->_info = Mage::getSingleton('paypal/info');
259:         try {
260:             // handle payment_status
261:             $paymentStatus = $this->_filterPaymentStatus($this->_request['payment_status']);
262: 
263:             switch ($paymentStatus) {
264:                 // paid
265:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_COMPLETED:
266:                     $this->_registerPaymentCapture();
267:                     break;
268: 
269:                 // the holded payment was denied on paypal side
270:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_DENIED:
271:                     $this->_registerPaymentDenial();
272:                     break;
273: 
274:                 // customer attempted to pay via bank account, but failed
275:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_FAILED:
276:                     // cancel order
277:                     $this->_registerPaymentFailure();
278:                     break;
279: 
280:                 // refund forced by PayPal
281:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_REVERSED: // break is intentionally omitted
282:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_UNREVERSED: // or returned back :)
283:                     $this->_registerPaymentReversal();
284:                     break;
285: 
286:                 // refund by merchant on PayPal side
287:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_REFUNDED:
288:                     $this->_registerPaymentRefund();
289:                     break;
290: 
291:                 // payment was obtained, but money were not captured yet
292:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_PENDING:
293:                     $this->_registerPaymentPending();
294:                     break;
295: 
296:                 // MassPayments success
297:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_PROCESSED:
298:                     $this->_registerMasspaymentsSuccess();
299:                     break;
300: 
301:                 // authorization expire/void
302:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_EXPIRED: // break is intentionally omitted
303:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_VOIDED:
304:                     $this->_registerPaymentVoid();
305:                     break;
306: 
307:                 default:
308:                     throw new Exception("Cannot handle payment status '{$paymentStatus}'.");
309:             }
310:         } catch (Mage_Core_Exception $e) {
311:             $comment = $this->_createIpnComment(Mage::helper('paypal')->__('Note: %s', $e->getMessage()), true);
312:             $comment->save();
313:             throw $e;
314:         }
315:     }
316: 
317:     /**
318:      * Process notification from recurring profile payments
319:      */
320:     protected function _processRecurringProfile()
321:     {
322:         $this->_recurringProfile = null;
323:         $this->_getRecurringProfile();
324: 
325:         try {
326:             // handle payment_status
327:             $paymentStatus = $this->_filterPaymentStatus($this->_request['payment_status']);
328: 
329:             switch ($paymentStatus) {
330:                 // paid
331:                 case Mage_Paypal_Model_Info::PAYMENTSTATUS_COMPLETED:
332:                     $this->_registerRecurringProfilePaymentCapture();
333:                     break;
334: 
335:                 default:
336:                     throw new Exception("Cannot handle payment status '{$paymentStatus}'.");
337:             }
338:         } catch (Mage_Core_Exception $e) {
339: // TODO: add to payment profile comments
340: //            $comment = $this->_createIpnComment(Mage::helper('paypal')->__('Note: %s', $e->getMessage()), true);
341: //            $comment->save();
342:             throw $e;
343:         }
344:     }
345: 
346:     /**
347:      * Register recurring payment notification, create and process order
348:      */
349:     protected function _registerRecurringProfilePaymentCapture()
350:     {
351:         $price = $this->getRequestData('mc_gross') - $this->getRequestData('tax') -  $this->getRequestData('shipping');
352:         $productItemInfo = new Varien_Object;
353:         $type = trim($this->getRequestData('period_type'));
354:         if ($type == 'Trial') {
355:             $productItemInfo->setPaymentType(Mage_Sales_Model_Recurring_Profile::PAYMENT_TYPE_TRIAL);
356:         } elseif ($type == 'Regular') {
357:             $productItemInfo->setPaymentType(Mage_Sales_Model_Recurring_Profile::PAYMENT_TYPE_REGULAR);
358:         }
359:         $productItemInfo->setTaxAmount($this->getRequestData('tax'));
360:         $productItemInfo->setShippingAmount($this->getRequestData('shipping'));
361:         $productItemInfo->setPrice($price);
362: 
363:         $order = $this->_recurringProfile->createOrder($productItemInfo);
364: 
365:         $payment = $order->getPayment();
366:         $payment->setTransactionId($this->getRequestData('txn_id'))
367:             ->setPreparedMessage($this->_createIpnComment(''))
368:             ->setIsTransactionClosed(0);
369:         $order->save();
370:         $this->_recurringProfile->addOrderRelation($order->getId());
371:         $payment->registerCaptureNotification($this->getRequestData('mc_gross'));
372:         $order->save();
373: 
374:         // notify customer
375:         if ($invoice = $payment->getCreatedInvoice()) {
376:             $message = Mage::helper('paypal')->__('Notified customer about invoice #%s.', $invoice->getIncrementId());
377:             $comment = $order->sendNewOrderEmail()->addStatusHistoryComment($message)
378:                 ->setIsCustomerNotified(true)
379:                 ->save();
380:         }
381:     }
382: 
383:     /**
384:      * Process completed payment (either full or partial)
385:      */
386:     protected function _registerPaymentCapture()
387:     {
388:         if ($this->getRequestData('transaction_entity') == 'auth') {
389:             return;
390:         }
391:         $this->_importPaymentInformation();
392:         $payment = $this->_order->getPayment();
393:         $payment->setTransactionId($this->getRequestData('txn_id'))
394:             ->setPreparedMessage($this->_createIpnComment(''))
395:             ->setParentTransactionId($this->getRequestData('parent_txn_id'))
396:             ->setShouldCloseParentTransaction('Completed' === $this->getRequestData('auth_status'))
397:             ->setIsTransactionClosed(0)
398:             ->registerCaptureNotification($this->getRequestData('mc_gross'));
399:         $this->_order->save();
400: 
401:         // notify customer
402:         $invoice = $payment->getCreatedInvoice();
403:         if ($invoice && !$this->_order->getEmailSent()) {
404:             $this->_order->sendNewOrderEmail()->addStatusHistoryComment(
405:                 Mage::helper('paypal')->__('Notified customer about invoice #%s.', $invoice->getIncrementId())
406:             )
407:             ->setIsCustomerNotified(true)
408:             ->save();
409:         }
410:     }
411: 
412:     /**
413:      * Process denied payment notification
414:      */
415:     protected function _registerPaymentDenial()
416:     {
417:         $this->_importPaymentInformation();
418:         $this->_order->getPayment()
419:             ->setTransactionId($this->getRequestData('txn_id'))
420:             ->setNotificationResult(true)
421:             ->setIsTransactionClosed(true)
422:             ->registerPaymentReviewAction(Mage_Sales_Model_Order_Payment::REVIEW_ACTION_DENY, false);
423:         $this->_order->save();
424:     }
425: 
426:     /**
427:      * Treat failed payment as order cancellation
428:      */
429:     protected function _registerPaymentFailure()
430:     {
431:         $this->_importPaymentInformation();
432:         $this->_order
433:             ->registerCancellation($this->_createIpnComment(''), false)
434:             ->save();
435:     }
436: 
437:     /**
438:      * Process a refund or a chargeback
439:      */
440:     protected function _registerPaymentRefund()
441:     {
442:         $this->_importPaymentInformation();
443:         $reason = $this->getRequestData('reason_code');
444:         $isRefundFinal = !$this->_info->isReversalDisputable($reason);
445:         $payment = $this->_order->getPayment()
446:             ->setPreparedMessage($this->_createIpnComment($this->_info->explainReasonCode($reason)))
447:             ->setTransactionId($this->getRequestData('txn_id'))
448:             ->setParentTransactionId($this->getRequestData('parent_txn_id'))
449:             ->setIsTransactionClosed($isRefundFinal)
450:             ->registerRefundNotification(-1 * $this->getRequestData('mc_gross'));
451:         $this->_order->save();
452: 
453:         // TODO: there is no way to close a capture right now
454: 
455:         if ($creditmemo = $payment->getCreatedCreditmemo()) {
456:             $creditmemo->sendEmail();
457:             $comment = $this->_order->addStatusHistoryComment(
458:                     Mage::helper('paypal')->__('Notified customer about creditmemo #%s.', $creditmemo->getIncrementId())
459:                 )
460:                 ->setIsCustomerNotified(true)
461:                 ->save();
462:         }
463:     }
464: 
465:     /**
466:      * Process payment reversal notification
467:      */
468:     protected function _registerPaymentReversal()
469:     {
470:         /**
471:          * PayPal may send such payment status when triggered IPR denial
472:          * Note that this check is done on the old payment info object, before importing new payment information
473:          */
474:         if ($this->_info->isPaymentReviewRequired($this->_order->getPayment())) {
475:             $this->_registerPaymentDenial();
476:             return;
477:         }
478: 
479:         if ('chargeback_reimbursement' == $this->getRequestData('reason_code')) {
480:             // TODO: chargebacks reversals are not implemented
481:             return;
482:         }
483: 
484:         // treat as a usual charegeback
485:         $this->_registerPaymentRefund();
486:     }
487: 
488:     /**
489:      * Process payment pending notification
490:      *
491:      * @throws Exception
492:      */
493:     public function _registerPaymentPending()
494:     {
495:         $reason = $this->getRequestData('pending_reason');
496:         if ('authorization' === $reason) {
497:             $this->_registerPaymentAuthorization();
498:             return;
499:         }
500:         if ('order' === $reason) {
501:             throw new Exception('The "order" authorizations are not implemented.');
502:         }
503: 
504:         // case when was placed using PayPal standard
505:         if (Mage_Sales_Model_Order::STATE_PENDING_PAYMENT == $this->_order->getState()) {
506:             $this->_registerPaymentCapture();
507:             return;
508:         }
509: 
510:         $this->_importPaymentInformation();
511: 
512:         $this->_order->getPayment()
513:             ->setPreparedMessage($this->_createIpnComment($this->_info->explainPendingReason($reason)))
514:             ->setTransactionId($this->getRequestData('txn_id'))
515:             ->setIsTransactionClosed(0)
516:             ->registerPaymentReviewAction(Mage_Sales_Model_Order_Payment::REVIEW_ACTION_UPDATE, false);
517:         $this->_order->save();
518:     }
519: 
520:     /**
521:      * Register authorized payment
522:      */
523:     protected function _registerPaymentAuthorization()
524:     {
525:         $this->_importPaymentInformation();
526: 
527:         $this->_order->getPayment()
528:             ->setPreparedMessage($this->_createIpnComment(''))
529:             ->setTransactionId($this->getRequestData('txn_id'))
530:             ->setParentTransactionId($this->getRequestData('parent_txn_id'))
531:             ->setIsTransactionClosed(0)
532:             ->registerAuthorizationNotification($this->getRequestData('mc_gross'));
533:         if (!$this->_order->getEmailSent()) {
534:             $this->_order->sendNewOrderEmail();
535:         }
536:         $this->_order->save();
537:     }
538: 
539:     /**
540:      * Process voided authorization
541:      */
542:     protected function _registerPaymentVoid()
543:     {
544:         $this->_importPaymentInformation();
545: 
546:         $parentTxnId = $this->getRequestData('transaction_entity') == 'auth'
547:             ? $this->getRequestData('txn_id') : $this->getRequestData('parent_txn_id');
548: 
549:         $this->_order->getPayment()
550:             ->setPreparedMessage($this->_createIpnComment(''))
551:             ->setParentTransactionId($parentTxnId)
552:             ->registerVoidNotification();
553: 
554:         $this->_order->save();
555:     }
556: 
557:     /**
558:      * TODO
559:      * The status "Processed" is used when all Masspayments are successful
560:      */
561:     protected function _registerMasspaymentsSuccess()
562:     {
563:         $comment = $this->_createIpnComment('', true);
564:         $comment->save();
565:     }
566: 
567:     /**
568:      * Generate an "IPN" comment with additional explanation.
569:      * Returns the generated comment or order status history object
570:      *
571:      * @param string $comment
572:      * @param bool $addToHistory
573:      * @return string|Mage_Sales_Model_Order_Status_History
574:      */
575:     protected function _createIpnComment($comment = '', $addToHistory = false)
576:     {
577:         $paymentStatus = $this->getRequestData('payment_status');
578:         $message = Mage::helper('paypal')->__('IPN "%s".', $paymentStatus);
579:         if ($comment) {
580:             $message .= ' ' . $comment;
581:         }
582:         if ($addToHistory) {
583:             $message = $this->_order->addStatusHistoryComment($message);
584:             $message->setIsCustomerNotified(null);
585:         }
586:         return $message;
587:     }
588: 
589:     /**
590:      * Map payment information from IPN to payment object
591:      * Returns true if there were changes in information
592:      *
593:      * @param Mage_Payment_Model_Info $payment
594:      * @return bool
595:      */
596:     protected function _importPaymentInformation()
597:     {
598:         $payment = $this->_order->getPayment();
599:         $was = $payment->getAdditionalInformation();
600: 
601:         // collect basic information
602:         $from = array();
603:         foreach (array(
604:             Mage_Paypal_Model_Info::PAYER_ID,
605:             'payer_email' => Mage_Paypal_Model_Info::PAYER_EMAIL,
606:             Mage_Paypal_Model_Info::PAYER_STATUS,
607:             Mage_Paypal_Model_Info::ADDRESS_STATUS,
608:             Mage_Paypal_Model_Info::PROTECTION_EL,
609:             Mage_Paypal_Model_Info::PAYMENT_STATUS,
610:             Mage_Paypal_Model_Info::PENDING_REASON,
611:         ) as $privateKey => $publicKey) {
612:             if (is_int($privateKey)) {
613:                 $privateKey = $publicKey;
614:             }
615:             $value = $this->getRequestData($privateKey);
616:             if ($value) {
617:                 $from[$publicKey] = $value;
618:             }
619:         }
620:         if (isset($from['payment_status'])) {
621:             $from['payment_status'] = $this->_filterPaymentStatus($this->getRequestData('payment_status'));
622:         }
623: 
624:         // collect fraud filters
625:         $fraudFilters = array();
626:         for ($i = 1; $value = $this->getRequestData("fraud_management_pending_filters_{$i}"); $i++) {
627:             $fraudFilters[] = $value;
628:         }
629:         if ($fraudFilters) {
630:             $from[Mage_Paypal_Model_Info::FRAUD_FILTERS] = $fraudFilters;
631:         }
632: 
633:         $this->_info->importToPayment($from, $payment);
634: 
635:         /**
636:          * Detect pending payment, frauds
637:          * TODO: implement logic in one place
638:          * @see Mage_Paypal_Model_Pro::importPaymentInfo()
639:          */
640:         if ($this->_info->isPaymentReviewRequired($payment)) {
641:             $payment->setIsTransactionPending(true);
642:             if ($fraudFilters) {
643:                 $payment->setIsFraudDetected(true);
644:             }
645:         }
646:         if ($this->_info->isPaymentSuccessful($payment)) {
647:             $payment->setIsTransactionApproved(true);
648:         } elseif ($this->_info->isPaymentFailed($payment)) {
649:             $payment->setIsTransactionDenied(true);
650:         }
651: 
652:         return $was != $payment->getAdditionalInformation();
653:     }
654: 
655:     /**
656:      * Filter payment status from NVP into paypal/info format
657:      *
658:      * @param string $ipnPaymentStatus
659:      * @return string
660:      */
661:     protected function _filterPaymentStatus($ipnPaymentStatus)
662:     {
663:         switch ($ipnPaymentStatus) {
664:             case 'Created': // break is intentionally omitted
665:             case 'Completed': return Mage_Paypal_Model_Info::PAYMENTSTATUS_COMPLETED;
666:             case 'Denied':    return Mage_Paypal_Model_Info::PAYMENTSTATUS_DENIED;
667:             case 'Expired':   return Mage_Paypal_Model_Info::PAYMENTSTATUS_EXPIRED;
668:             case 'Failed':    return Mage_Paypal_Model_Info::PAYMENTSTATUS_FAILED;
669:             case 'Pending':   return Mage_Paypal_Model_Info::PAYMENTSTATUS_PENDING;
670:             case 'Refunded':  return Mage_Paypal_Model_Info::PAYMENTSTATUS_REFUNDED;
671:             case 'Reversed':  return Mage_Paypal_Model_Info::PAYMENTSTATUS_REVERSED;
672:             case 'Canceled_Reversal': return Mage_Paypal_Model_Info::PAYMENTSTATUS_UNREVERSED;
673:             case 'Processed': return Mage_Paypal_Model_Info::PAYMENTSTATUS_PROCESSED;
674:             case 'Voided':    return Mage_Paypal_Model_Info::PAYMENTSTATUS_VOIDED;
675:         }
676:         return '';
677: // documented in NVP, but not documented in IPN:
678: //Mage_Paypal_Model_Info::PAYMENTSTATUS_NONE
679: //Mage_Paypal_Model_Info::PAYMENTSTATUS_INPROGRESS
680: //Mage_Paypal_Model_Info::PAYMENTSTATUS_REFUNDEDPART
681:     }
682: 
683:     /**
684:      * Log debug data to file
685:      *
686:      * @param mixed $debugData
687:      */
688:     protected function _debug()
689:     {
690:         if ($this->_config && $this->_config->debug) {
691:             $file = $this->_config->getMethodCode() ? "payment_{$this->_config->getMethodCode()}.log"
692:                 : self::DEFAULT_LOG_FILE;
693:             Mage::getModel('core/log_adapter', $file)->log($this->_debugData);
694:         }
695:     }
696: }
697: 
Magento 1.7.0.2 API documentation generated by ApiGen 2.8.0