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:  * Wrapper that performs Paypal Express and Checkout communication
 29:  * Use current Paypal Express method instance
 30:  */
 31: class Mage_Paypal_Model_Express_Checkout
 32: {
 33:     /**
 34:      * Cache ID prefix for "pal" lookup
 35:      * @var string
 36:      */
 37:     const PAL_CACHE_ID = 'paypal_express_checkout_pal';
 38: 
 39:     /**
 40:      * Keys for passthrough variables in sales/quote_payment and sales/order_payment
 41:      * Uses additional_information as storage
 42:      * @var string
 43:      */
 44:     const PAYMENT_INFO_TRANSPORT_TOKEN    = 'paypal_express_checkout_token';
 45:     const PAYMENT_INFO_TRANSPORT_SHIPPING_OVERRIDEN = 'paypal_express_checkout_shipping_overriden';
 46:     const PAYMENT_INFO_TRANSPORT_SHIPPING_METHOD = 'paypal_express_checkout_shipping_method';
 47:     const PAYMENT_INFO_TRANSPORT_PAYER_ID = 'paypal_express_checkout_payer_id';
 48:     const PAYMENT_INFO_TRANSPORT_REDIRECT = 'paypal_express_checkout_redirect_required';
 49:     const PAYMENT_INFO_TRANSPORT_BILLING_AGREEMENT = 'paypal_ec_create_ba';
 50: 
 51:     /**
 52:      * @var Mage_Sales_Model_Quote
 53:      */
 54:     protected $_quote = null;
 55: 
 56:     /**
 57:      * Config instance
 58:      * @var Mage_Paypal_Model_Config
 59:      */
 60:     protected $_config = null;
 61: 
 62:     /**
 63:      * API instance
 64:      * @var Mage_Paypal_Model_Api_Nvp
 65:      */
 66:     protected $_api = null;
 67: 
 68:     /**
 69:      * Api Model Type
 70:      *
 71:      * @var string
 72:      */
 73:     protected $_apiType = 'paypal/api_nvp';
 74: 
 75:     /**
 76:      * Payment method type
 77:      *
 78:      * @var unknown_type
 79:      */
 80:     protected $_methodType = Mage_Paypal_Model_Config::METHOD_WPP_EXPRESS;
 81: 
 82:     /**
 83:      * State helper variables
 84:      * @var string
 85:      */
 86:     protected $_redirectUrl = '';
 87:     protected $_pendingPaymentMessage = '';
 88:     protected $_checkoutRedirectUrl = '';
 89: 
 90:     /**
 91:      * @var Mage_Customer_Model_Session
 92:      */
 93:     protected $_customerSession;
 94: 
 95:     /**
 96:      * Redirect urls supposed to be set to support giropay
 97:      *
 98:      * @var array
 99:      */
100:     protected $_giropayUrls = array();
101: 
102:     /**
103:      * Create Billing Agreement flag
104:      *
105:      * @var bool
106:      */
107:     protected $_isBARequested = false;
108: 
109:     /**
110:      * Customer ID
111:      *
112:      * @var int
113:      */
114:     protected $_customerId = null;
115: 
116:     /**
117:      * Recurring payment profiles
118:      *
119:      * @var array
120:      */
121:     protected $_recurringPaymentProfiles = array();
122: 
123:     /**
124:      * Billing agreement that might be created during order placing
125:      *
126:      * @var Mage_Sales_Model_Billing_Agreement
127:      */
128:     protected $_billingAgreement = null;
129: 
130:     /**
131:      * Order
132:      *
133:      * @var Mage_Sales_Model_QuoteMage_Sales_Model_Quote
134:      */
135:     protected $_order = null;
136: 
137:     /**
138:      * Set quote and config instances
139:      * @param array $params
140:      */
141:     public function __construct($params = array())
142:     {
143:         if (isset($params['quote']) && $params['quote'] instanceof Mage_Sales_Model_Quote) {
144:             $this->_quote = $params['quote'];
145:         } else {
146:             throw new Exception('Quote instance is required.');
147:         }
148:         if (isset($params['config']) && $params['config'] instanceof Mage_Paypal_Model_Config) {
149:             $this->_config = $params['config'];
150:         } else {
151:             throw new Exception('Config instance is required.');
152:         }
153:         $this->_customerSession = Mage::getSingleton('customer/session');
154:     }
155: 
156:     /**
157:      * Checkout with PayPal image URL getter
158:      * Spares API calls of getting "pal" variable, by putting it into cache per store view
159:      * @return string
160:      */
161:     public function getCheckoutShortcutImageUrl()
162:     {
163:         // get "pal" thing from cache or lookup it via API
164:         $pal = null;
165:         if ($this->_config->areButtonsDynamic()) {
166:             $cacheId = self::PAL_CACHE_ID . Mage::app()->getStore()->getId();
167:             $pal = Mage::app()->loadCache($cacheId);
168:             if (-1 == $pal) {
169:                 $pal = null;
170:             } elseif (!$pal) {
171:                 $pal = null;
172:                 $this->_getApi();
173:                 try {
174:                     $this->_api->callGetPalDetails();
175:                     $pal = $this->_api->getPal();
176:                     Mage::app()->saveCache($pal, $cacheId, array(Mage_Core_Model_Config::CACHE_TAG));
177:                 } catch (Exception $e) {
178:                     Mage::app()->saveCache(-1, $cacheId, array(Mage_Core_Model_Config::CACHE_TAG));
179:                     Mage::logException($e);
180:                 }
181:             }
182:         }
183: 
184:         return $this->_config->getExpressCheckoutShortcutImageUrl(
185:             Mage::app()->getLocale()->getLocaleCode(),
186:             $this->_quote->getBaseGrandTotal(),
187:             $pal
188:         );
189:     }
190: 
191:     /**
192:      * Setter that enables giropay redirects flow
193:      *
194:      * @param string $successUrl - payment success result
195:      * @param string $cancelUrl  - payment cancellation result
196:      * @param string $pendingUrl - pending payment result
197:      * @return Mage_Paypal_Model_Express_Checkout
198:      */
199:     public function prepareGiropayUrls($successUrl, $cancelUrl, $pendingUrl)
200:     {
201:         $this->_giropayUrls = array($successUrl, $cancelUrl, $pendingUrl);
202:         return $this;
203:     }
204: 
205:     /**
206:      * Set create billing agreement flag
207:      *
208:      * @param bool $flag
209:      * @return Mage_Paypal_Model_Express_Checkout
210:      */
211:     public function setIsBillingAgreementRequested($flag)
212:     {
213:         $this->_isBARequested = $flag;
214:         return $this;
215:     }
216: 
217:     /**
218:      * Setter for customer Id
219:      *
220:      * @param int $id
221:      * @return Mage_Paypal_Model_Express_Checkout
222:      * @deprecated please use self::setCustomer
223:      */
224:     public function setCustomerId($id)
225:     {
226:         $this->_customerId = $id;
227:         return $this;
228:     }
229: 
230:     /**
231:      * Setter for customer
232:      *
233:      * @param Mage_Customer_Model_Customer $customer
234:      * @return Mage_Paypal_Model_Express_Checkout
235:      */
236:     public function setCustomer($customer)
237:     {
238:         $this->_quote->assignCustomer($customer);
239:         $this->_customerId = $customer->getId();
240:         return $this;
241:     }
242: 
243:     /**
244:      * Setter for customer with billing and shipping address changing ability
245:      *
246:      * @param  Mage_Customer_Model_Customer   $customer
247:      * @param  Mage_Sales_Model_Quote_Address $billingAddress
248:      * @param  Mage_Sales_Model_Quote_Address $shippingAddress
249:      * @return Mage_Paypal_Model_Express_Checkout
250:      */
251:     public function setCustomerWithAddressChange($customer, $billingAddress = null, $shippingAddress = null)
252:     {
253:         $this->_quote->assignCustomerWithAddressChange($customer, $billingAddress, $shippingAddress);
254:         $this->_customerId = $customer->getId();
255:         return $this;
256:     }
257: 
258:     /**
259:      * Reserve order ID for specified quote and start checkout on PayPal
260:      *
261:      * @param string $returnUrl
262:      * @param string $cancelUrl
263:      * @return mixed
264:      */
265:     public function start($returnUrl, $cancelUrl)
266:     {
267:         $this->_quote->collectTotals();
268: 
269:         if (!$this->_quote->getGrandTotal() && !$this->_quote->hasNominalItems()) {
270:             Mage::throwException(Mage::helper('paypal')->__('PayPal does not support processing orders with zero amount. To complete your purchase, proceed to the standard checkout process.'));
271:         }
272: 
273:         $this->_quote->reserveOrderId()->save();
274:         // prepare API
275:         $this->_getApi();
276:         $this->_api->setAmount($this->_quote->getBaseGrandTotal())
277:             ->setCurrencyCode($this->_quote->getBaseCurrencyCode())
278:             ->setInvNum($this->_quote->getReservedOrderId())
279:             ->setReturnUrl($returnUrl)
280:             ->setCancelUrl($cancelUrl)
281:             ->setSolutionType($this->_config->solutionType)
282:             ->setPaymentAction($this->_config->paymentAction)
283:         ;
284:         if ($this->_giropayUrls) {
285:             list($successUrl, $cancelUrl, $pendingUrl) = $this->_giropayUrls;
286:             $this->_api->addData(array(
287:                 'giropay_cancel_url' => $cancelUrl,
288:                 'giropay_success_url' => $successUrl,
289:                 'giropay_bank_txn_pending_url' => $pendingUrl,
290:             ));
291:         }
292: 
293:         $this->_setBillingAgreementRequest();
294: 
295:         if ($this->_config->requireBillingAddress == Mage_Paypal_Model_Config::REQUIRE_BILLING_ADDRESS_ALL) {
296:             $this->_api->setRequireBillingAddress(1);
297:         }
298: 
299:         // supress or export shipping address
300:         if ($this->_quote->getIsVirtual()) {
301:             if ($this->_config->requireBillingAddress == Mage_Paypal_Model_Config::REQUIRE_BILLING_ADDRESS_VIRTUAL) {
302:                 $this->_api->setRequireBillingAddress(1);
303:             }
304:             $this->_api->setSuppressShipping(true);
305:         } else {
306:             $address = $this->_quote->getShippingAddress();
307:             $isOverriden = 0;
308:             if (true === $address->validate()) {
309:                 $isOverriden = 1;
310:                 $this->_api->setAddress($address);
311:             }
312:             $this->_quote->getPayment()->setAdditionalInformation(
313:                 self::PAYMENT_INFO_TRANSPORT_SHIPPING_OVERRIDEN, $isOverriden
314:             );
315:             $this->_quote->getPayment()->save();
316:         }
317: 
318:         // add line items
319:         $paypalCart = Mage::getModel('paypal/cart', array($this->_quote));
320:         $this->_api->setPaypalCart($paypalCart)
321:             ->setIsLineItemsEnabled($this->_config->lineItemsEnabled)
322:         ;
323: 
324:         // add shipping options if needed and line items are available
325:         if ($this->_config->lineItemsEnabled && $this->_config->transferShippingOptions && $paypalCart->getItems()) {
326:             if (!$this->_quote->getIsVirtual() && !$this->_quote->hasNominalItems()) {
327:                 if ($options = $this->_prepareShippingOptions($address, true)) {
328:                     $this->_api->setShippingOptionsCallbackUrl(
329:                         Mage::getUrl('*/*/shippingOptionsCallback', array('quote_id' => $this->_quote->getId()))
330:                     )->setShippingOptions($options);
331:                 }
332:             }
333:         }
334: 
335:         // add recurring payment profiles information
336:         if ($profiles = $this->_quote->prepareRecurringPaymentProfiles()) {
337:             foreach ($profiles as $profile) {
338:                 $profile->setMethodCode(Mage_Paypal_Model_Config::METHOD_WPP_EXPRESS);
339:                 if (!$profile->isValid()) {
340:                     Mage::throwException($profile->getValidationErrors(true, true));
341:                 }
342:             }
343:             $this->_api->addRecurringPaymentProfiles($profiles);
344:         }
345: 
346:         $this->_config->exportExpressCheckoutStyleSettings($this->_api);
347: 
348:         // call API and redirect with token
349:         $this->_api->callSetExpressCheckout();
350:         $token = $this->_api->getToken();
351:         $this->_redirectUrl = $this->_config->getExpressCheckoutStartUrl($token);
352: 
353:         $this->_quote->getPayment()->unsAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_BILLING_AGREEMENT);
354:         $this->_quote->getPayment()->save();
355:         return $token;
356:     }
357: 
358:     /**
359:      * Update quote when returned from PayPal
360:      * rewrite billing address by paypal
361:      * save old billing address for new customer
362:      * export shipping address in case address absence
363:      *
364:      * @param string $token
365:      */
366:     public function returnFromPaypal($token)
367:     {
368:         $this->_getApi();
369:         $this->_api->setToken($token)
370:             ->callGetExpressCheckoutDetails();
371:         $quote = $this->_quote;
372: 
373:         $this->_ignoreAddressValidation();
374: 
375:         // import billing address
376:         $billingAddress = $quote->getBillingAddress();
377:         $exportedBillingAddress = $this->_api->getExportedBillingAddress();
378:         $quote->setCustomerEmail($billingAddress->getEmail());
379:         $quote->setCustomerPrefix($billingAddress->getPrefix());
380:         $quote->setCustomerFirstname($billingAddress->getFirstname());
381:         $quote->setCustomerMiddlename($billingAddress->getMiddlename());
382:         $quote->setCustomerLastname($billingAddress->getLastname());
383:         $quote->setCustomerSuffix($billingAddress->getSuffix());
384:         $quote->setCustomerNote($exportedBillingAddress->getData('note'));
385:         $this->_setExportedAddressData($billingAddress, $exportedBillingAddress);
386: 
387:         // import shipping address
388:         $exportedShippingAddress = $this->_api->getExportedShippingAddress();
389:         if (!$quote->getIsVirtual()) {
390:             $shippingAddress = $quote->getShippingAddress();
391:             if ($shippingAddress) {
392:                 if ($exportedShippingAddress) {
393:                     $this->_setExportedAddressData($shippingAddress, $exportedShippingAddress);
394:                     $shippingAddress->setCollectShippingRates(true);
395:                     $shippingAddress->setSameAsBilling(0);
396:                 }
397: 
398:                 // import shipping method
399:                 $code = '';
400:                 if ($this->_api->getShippingRateCode()) {
401:                     if ($code = $this->_matchShippingMethodCode($shippingAddress, $this->_api->getShippingRateCode())) {
402:                          // possible bug of double collecting rates :-/
403:                         $shippingAddress->setShippingMethod($code)->setCollectShippingRates(true);
404:                     }
405:                 }
406:                 $quote->getPayment()->setAdditionalInformation(
407:                     self::PAYMENT_INFO_TRANSPORT_SHIPPING_METHOD,
408:                     $code
409:                 );
410:             }
411:         }
412: 
413:         // import payment info
414:         $payment = $quote->getPayment();
415:         $payment->setMethod($this->_methodType);
416:         Mage::getSingleton('paypal/info')->importToPayment($this->_api, $payment);
417:         $payment->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_PAYER_ID, $this->_api->getPayerId())
418:             ->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_TOKEN, $token)
419:         ;
420:         $quote->collectTotals()->save();
421:     }
422: 
423:     /**
424:      * Check whether order review has enough data to initialize
425:      *
426:      * @param $token
427:      * @throws Mage_Core_Exception
428:      */
429:     public function prepareOrderReview($token = null)
430:     {
431:         $payment = $this->_quote->getPayment();
432:         if (!$payment || !$payment->getAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_PAYER_ID)) {
433:             Mage::throwException(Mage::helper('paypal')->__('Payer is not identified.'));
434:         }
435:         $this->_quote->setMayEditShippingAddress(
436:             1 != $this->_quote->getPayment()->getAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_SHIPPING_OVERRIDEN)
437:         );
438:         $this->_quote->setMayEditShippingMethod(
439:             '' == $this->_quote->getPayment()->getAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_SHIPPING_METHOD)
440:         );
441:         $this->_ignoreAddressValidation();
442:         $this->_quote->collectTotals()->save();
443:     }
444: 
445:     /**
446:      * Return callback response with shipping options
447:      *
448:      * @param array $request
449:      * @return string
450:      */
451:     public function getShippingOptionsCallbackResponse(array $request)
452:     {
453:         // prepare debug data
454:         $logger = Mage::getModel('core/log_adapter', 'payment_' . $this->_methodType . '.log');
455:         $debugData = array('request' => $request, 'response' => array());
456: 
457:         try {
458:             // obtain addresses
459:             $this->_getApi();
460:             $address = $this->_api->prepareShippingOptionsCallbackAddress($request);
461:             $quoteAddress = $this->_quote->getShippingAddress();
462: 
463:             // compare addresses, calculate shipping rates and prepare response
464:             $options = array();
465:             if ($address && $quoteAddress && !$this->_quote->getIsVirtual()) {
466:                 foreach ($address->getExportedKeys() as $key) {
467:                     $quoteAddress->setDataUsingMethod($key, $address->getData($key));
468:                 }
469:                 $quoteAddress->setCollectShippingRates(true)->collectTotals();
470:                 $options = $this->_prepareShippingOptions($quoteAddress, false, true);
471:             }
472:             $response = $this->_api->setShippingOptions($options)->formatShippingOptionsCallback();
473: 
474:             // log request and response
475:             $debugData['response'] = $response;
476:             $logger->log($debugData);
477:             return $response;
478:         } catch (Exception $e) {
479:             $logger->log($debugData);
480:             throw $e;
481:         }
482:     }
483: 
484:     /**
485:      * Set shipping method to quote, if needed
486:      * @param string $methodCode
487:      */
488:     public function updateShippingMethod($methodCode)
489:     {
490:         if (!$this->_quote->getIsVirtual() && $shippingAddress = $this->_quote->getShippingAddress()) {
491:             if ($methodCode != $shippingAddress->getShippingMethod()) {
492:                 $this->_ignoreAddressValidation();
493:                 $shippingAddress->setShippingMethod($methodCode)->setCollectShippingRates(true);
494:                 $this->_quote->collectTotals();
495:             }
496:         }
497:     }
498: 
499:     /**
500:      * Update order data
501:      *
502:      * @param array $data
503:      */
504:     public function updateOrder($data)
505:     {
506:         /** @var $checkout Mage_Checkout_Model_Type_Onepage */
507:         $checkout = Mage::getModel('checkout/type_onepage');
508: 
509:         $this->_quote->setTotalsCollectedFlag(true);
510:         $checkout->setQuote($this->_quote);
511:         if (isset($data['billing'])) {
512:             if (isset($data['customer-email'])) {
513:                 $data['billing']['email'] = $data['customer-email'];
514:             }
515:             $checkout->saveBilling($data['billing'], 0);
516:         }
517:         if (!$this->_quote->getIsVirtual() && isset($data['shipping'])) {
518:             $checkout->saveShipping($data['shipping'], 0);
519:         }
520: 
521:         if (isset($data['shipping_method'])) {
522:             $this->updateShippingMethod($data['shipping_method']);
523:         }
524:         $this->_quote->setTotalsCollectedFlag(false);
525:         $this->_quote->collectTotals();
526:         $this->_quote->setDataChanges(true);
527:         $this->_quote->save();
528:     }
529: 
530:     /**
531:      * Place the order and recurring payment profiles when customer returned from paypal
532:      * Until this moment all quote data must be valid
533:      *
534:      * @param string $token
535:      * @param string $shippingMethodCode
536:      */
537:     public function place($token, $shippingMethodCode = null)
538:     {
539:         if ($shippingMethodCode) {
540:             $this->updateShippingMethod($shippingMethodCode);
541:         }
542: 
543:         $isNewCustomer = false;
544:         switch ($this->getCheckoutMethod()) {
545:             case Mage_Checkout_Model_Type_Onepage::METHOD_GUEST:
546:                 $this->_prepareGuestQuote();
547:                 break;
548:             case Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER:
549:                 $this->_prepareNewCustomerQuote();
550:                 $isNewCustomer = true;
551:                 break;
552:             default:
553:                 $this->_prepareCustomerQuote();
554:                 break;
555:         }
556: 
557:         $this->_ignoreAddressValidation();
558:         $this->_quote->collectTotals();
559:         $service = Mage::getModel('sales/service_quote', $this->_quote);
560:         $service->submitAll();
561:         $this->_quote->save();
562: 
563:         if ($isNewCustomer) {
564:             try {
565:                 $this->_involveNewCustomer();
566:             } catch (Exception $e) {
567:                 Mage::logException($e);
568:             }
569:         }
570: 
571:         $this->_recurringPaymentProfiles = $service->getRecurringPaymentProfiles();
572:         // TODO: send recurring profile emails
573: 
574:         $order = $service->getOrder();
575:         if (!$order) {
576:             return;
577:         }
578:         $this->_billingAgreement = $order->getPayment()->getBillingAgreement();
579: 
580:         // commence redirecting to finish payment, if paypal requires it
581:         if ($order->getPayment()->getAdditionalInformation(
582:                 Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_REDIRECT
583:         )) {
584:             $this->_redirectUrl = $this->_config->getExpressCheckoutCompleteUrl($token);
585:         }
586: 
587:         switch ($order->getState()) {
588:             // even after placement paypal can disallow to authorize/capture, but will wait until bank transfers money
589:             case Mage_Sales_Model_Order::STATE_PENDING_PAYMENT:
590:                 // TODO
591:                 break;
592:             // regular placement, when everything is ok
593:             case Mage_Sales_Model_Order::STATE_PROCESSING:
594:             case Mage_Sales_Model_Order::STATE_COMPLETE:
595:             case Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW:
596:                 $order->sendNewOrderEmail();
597:                 break;
598:         }
599:         $this->_order = $order;
600:     }
601: 
602:     /**
603:      * Make sure addresses will be saved without validation errors
604:      */
605:     private function _ignoreAddressValidation()
606:     {
607:         $this->_quote->getBillingAddress()->setShouldIgnoreValidation(true);
608:         if (!$this->_quote->getIsVirtual()) {
609:             $this->_quote->getShippingAddress()->setShouldIgnoreValidation(true);
610:             if (!$this->_config->requireBillingAddress && !$this->_quote->getBillingAddress()->getEmail()) {
611:                 $this->_quote->getBillingAddress()->setSameAsBilling(1);
612:             }
613:         }
614:     }
615: 
616:     /**
617:      * Determine whether redirect somewhere specifically is required
618:      *
619:      * @return string
620:      */
621:     public function getRedirectUrl()
622:     {
623:         return $this->_redirectUrl;
624:     }
625: 
626:     /**
627:      * Return recurring payment profiles
628:      *
629:      * @return array
630:      */
631:     public function getRecurringPaymentProfiles()
632:     {
633:         return $this->_recurringPaymentProfiles;
634:     }
635: 
636:     /**
637:      * Get created billing agreement
638:      *
639:      * @return Mage_Sales_Model_Billing_Agreement|null
640:      */
641:     public function getBillingAgreement()
642:     {
643:         return $this->_billingAgreement;
644:     }
645: 
646:     /**
647:      * Return order
648:      *
649:      * @return Mage_Sales_Model_Order
650:      */
651:     public function getOrder()
652:     {
653:         return $this->_order;
654:     }
655: 
656:     /**
657:      * Get checkout method
658:      *
659:      * @return string
660:      */
661:     public function getCheckoutMethod()
662:     {
663:         if ($this->getCustomerSession()->isLoggedIn()) {
664:             return Mage_Checkout_Model_Type_Onepage::METHOD_CUSTOMER;
665:         }
666:         if (!$this->_quote->getCheckoutMethod()) {
667:             if (Mage::helper('checkout')->isAllowedGuestCheckout($this->_quote)) {
668:                 $this->_quote->setCheckoutMethod(Mage_Checkout_Model_Type_Onepage::METHOD_GUEST);
669:             } else {
670:                 $this->_quote->setCheckoutMethod(Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER);
671:             }
672:         }
673:         return $this->_quote->getCheckoutMethod();
674:     }
675: 
676:     /**
677:      * Sets address data from exported address
678:      *
679:      * @param Mage_Sales_Model_Quote_Address $address
680:      * @param array $exportedAddress
681:      */
682:     protected function _setExportedAddressData($address, $exportedAddress)
683:     {
684:         foreach ($exportedAddress->getExportedKeys() as $key) {
685:             $oldData = $address->getDataUsingMethod($key);
686:             $isEmpty = null;
687:             if (is_array($oldData)) {
688:                 foreach($oldData as $val) {
689:                     if(!empty($val)) {
690:                         $isEmpty = false;
691:                         break;
692:                     }
693:                     $isEmpty = true;
694:                 }
695:             }
696:             if (empty($oldData) || $isEmpty === true) {
697:                 $address->setDataUsingMethod($key, $exportedAddress->getData($key));
698:             }
699:         }
700:     }
701: 
702:     /**
703:      * Set create billing agreement flag to api call
704:      *
705:      * @return Mage_Paypal_Model_Express_Checkout
706:      */
707:     protected function _setBillingAgreementRequest()
708:     {
709:         if (!$this->_customerId || $this->_quote->hasNominalItems()) {
710:             return $this;
711:         }
712: 
713:         $isRequested = $this->_isBARequested || $this->_quote->getPayment()
714:             ->getAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_BILLING_AGREEMENT);
715: 
716:         if (!($this->_config->allow_ba_signup == Mage_Paypal_Model_Config::EC_BA_SIGNUP_AUTO
717:             || $isRequested && $this->_config->shouldAskToCreateBillingAgreement())) {
718:             return $this;
719:         }
720: 
721:         if (!Mage::getModel('sales/billing_agreement')->needToCreateForCustomer($this->_customerId)) {
722:             return $this;
723:         }
724:         $this->_api->setBillingType($this->_api->getBillingAgreementType());
725:         return $this;
726:     }
727: 
728:     /**
729:      * @return Mage_Paypal_Model_Api_Nvp
730:      */
731:     protected function _getApi()
732:     {
733:         if (null === $this->_api) {
734:             $this->_api = Mage::getModel($this->_apiType)->setConfigObject($this->_config);
735:         }
736:         return $this->_api;
737:     }
738: 
739:     /**
740:      * Attempt to collect address shipping rates and return them for further usage in instant update API
741:      * Returns empty array if it was impossible to obtain any shipping rate
742:      * If there are shipping rates obtained, the method must return one of them as default.
743:      *
744:      * @param Mage_Sales_Model_Quote_Address $address
745:      * @param bool $mayReturnEmpty
746:      * @return array|false
747:      */
748:     protected function _prepareShippingOptions(
749:         Mage_Sales_Model_Quote_Address $address,
750:         $mayReturnEmpty = false, $calculateTax = false
751:     ) {
752:         $options = array(); $i = 0; $iMin = false; $min = false;
753:         $userSelectedOption = null;
754: 
755:         foreach ($address->getGroupedAllShippingRates() as $group) {
756:             foreach ($group as $rate) {
757:                 $amount = (float)$rate->getPrice();
758:                 if ($rate->getErrorMessage()) {
759:                     continue;
760:                 }
761:                 $isDefault = $address->getShippingMethod() === $rate->getCode();
762:                 $amountExclTax = Mage::helper('tax')->getShippingPrice($amount, false, $address);
763:                 $amountInclTax = Mage::helper('tax')->getShippingPrice($amount, true, $address);
764: 
765:                 $options[$i] = new Varien_Object(array(
766:                     'is_default' => $isDefault,
767:                     'name'       => trim("{$rate->getCarrierTitle()} - {$rate->getMethodTitle()}", ' -'),
768:                     'code'       => $rate->getCode(),
769:                     'amount'     => $amountExclTax,
770:                 ));
771:                 if ($calculateTax) {
772:                     $options[$i]->setTaxAmount(
773:                         $amountInclTax - $amountExclTax
774:                             + $address->getTaxAmount() - $address->getShippingTaxAmount()
775:                     );
776:                 }
777:                 if ($isDefault) {
778:                     $userSelectedOption = $options[$i];
779:                 }
780:                 if (false === $min || $amountInclTax < $min) {
781:                     $min = $amountInclTax;
782:                     $iMin = $i;
783:                 }
784:                 $i++;
785:             }
786:         }
787: 
788:         if ($mayReturnEmpty && is_null($userSelectedOption)) {
789:             $options[] = new Varien_Object(array(
790:                 'is_default' => true,
791:                 'name'       => Mage::helper('paypal')->__('N/A'),
792:                 'code'       => 'no_rate',
793:                 'amount'     => 0.00,
794:             ));
795:             if ($calculateTax) {
796:                 $options[$i]->setTaxAmount($address->getTaxAmount());
797:             }
798:         } elseif (is_null($userSelectedOption) && isset($options[$iMin])) {
799:             $options[$iMin]->setIsDefault(true);
800:         }
801: 
802:         // Magento will transfer only first 10 cheapest shipping options if there are more than 10 available.
803:         if (count($options) > 10) {
804:             usort($options, array(get_class($this),'cmpShippingOptions'));
805:             array_splice($options, 10);
806:             // User selected option will be always included in options list
807:             if (!is_null($userSelectedOption) && !in_array($userSelectedOption, $options)) {
808:                 $options[9] = $userSelectedOption;
809:             }
810:         }
811: 
812:         return $options;
813:     }
814: 
815:     /**
816:      * Compare two shipping options based on their amounts
817:      *
818:      * This function is used as a callback comparison function in shipping options sorting process
819:      * @see self::_prepareShippingOptions()
820:      *
821:      * @param Varien_Object $option1
822:      * @param Varien_Object $option2
823:      * @return integer
824:      */
825:     protected static function cmpShippingOptions(Varien_Object $option1, Varien_Object $option2)
826:     {
827:         if ($option1->getAmount() == $option2->getAmount()) {
828:             return 0;
829:         }
830:         return ($option1->getAmount() < $option2->getAmount()) ? -1 : 1;
831:     }
832: 
833:     /**
834:      * Try to find whether the code provided by PayPal corresponds to any of possible shipping rates
835:      * This method was created only because PayPal has issues with returning the selected code.
836:      * If in future the issue is fixed, we don't need to attempt to match it. It would be enough to set the method code
837:      * before collecting shipping rates
838:      *
839:      * @param Mage_Sales_Model_Quote_Address $address
840:      * @param string $selectedCode
841:      * @return string
842:      */
843:     protected function _matchShippingMethodCode(Mage_Sales_Model_Quote_Address $address, $selectedCode)
844:     {
845:         $options = $this->_prepareShippingOptions($address, false);
846:         foreach ($options as $option) {
847:             if ($selectedCode === $option['code'] // the proper case as outlined in documentation
848:                 || $selectedCode === $option['name'] // workaround: PayPal may return name instead of the code
849:                 // workaround: PayPal may concatenate code and name, and return it instead of the code:
850:                 || $selectedCode === "{$option['code']} {$option['name']}"
851:             ) {
852:                 return $option['code'];
853:             }
854:         }
855:         return '';
856:     }
857: 
858:     /**
859:      * Prepare quote for guest checkout order submit
860:      *
861:      * @return Mage_Paypal_Model_Express_Checkout
862:      */
863:     protected function _prepareGuestQuote()
864:     {
865:         $quote = $this->_quote;
866:         $quote->setCustomerId(null)
867:             ->setCustomerEmail($quote->getBillingAddress()->getEmail())
868:             ->setCustomerIsGuest(true)
869:             ->setCustomerGroupId(Mage_Customer_Model_Group::NOT_LOGGED_IN_ID);
870:         return $this;
871:     }
872: 
873:     /**
874:      * Prepare quote for customer registration and customer order submit
875:      * and restore magento customer data from quote
876:      *
877:      * @return Mage_Paypal_Model_Express_Checkout
878:      */
879:     protected function _prepareNewCustomerQuote()
880:     {
881:         $quote      = $this->_quote;
882:         $billing    = $quote->getBillingAddress();
883:         $shipping   = $quote->isVirtual() ? null : $quote->getShippingAddress();
884: 
885:         $customer = $quote->getCustomer();
886:         /** @var $customer Mage_Customer_Model_Customer */
887:         $customerBilling = $billing->exportCustomerAddress();
888:         $customer->addAddress($customerBilling);
889:         $billing->setCustomerAddress($customerBilling);
890:         $customerBilling->setIsDefaultBilling(true);
891:         if ($shipping && !$shipping->getSameAsBilling()) {
892:             $customerShipping = $shipping->exportCustomerAddress();
893:             $customer->addAddress($customerShipping);
894:             $shipping->setCustomerAddress($customerShipping);
895:             $customerShipping->setIsDefaultShipping(true);
896:         } elseif ($shipping) {
897:             $customerBilling->setIsDefaultShipping(true);
898:         }
899:         /**
900:          * @todo integration with dynamica attributes customer_dob, customer_taxvat, customer_gender
901:          */
902:         if ($quote->getCustomerDob() && !$billing->getCustomerDob()) {
903:             $billing->setCustomerDob($quote->getCustomerDob());
904:         }
905: 
906:         if ($quote->getCustomerTaxvat() && !$billing->getCustomerTaxvat()) {
907:             $billing->setCustomerTaxvat($quote->getCustomerTaxvat());
908:         }
909: 
910:         if ($quote->getCustomerGender() && !$billing->getCustomerGender()) {
911:             $billing->setCustomerGender($quote->getCustomerGender());
912:         }
913: 
914:         Mage::helper('core')->copyFieldset('checkout_onepage_billing', 'to_customer', $billing, $customer);
915:         $customer->setEmail($quote->getCustomerEmail());
916:         $customer->setPrefix($quote->getCustomerPrefix());
917:         $customer->setFirstname($quote->getCustomerFirstname());
918:         $customer->setMiddlename($quote->getCustomerMiddlename());
919:         $customer->setLastname($quote->getCustomerLastname());
920:         $customer->setSuffix($quote->getCustomerSuffix());
921:         $customer->setPassword($customer->decryptPassword($quote->getPasswordHash()));
922:         $customer->setPasswordHash($customer->hashPassword($customer->getPassword()));
923:         $customer->save();
924:         $quote->setCustomer($customer);
925: 
926:         return $this;
927:     }
928: 
929:     /**
930:      * Prepare quote for customer order submit
931:      *
932:      * @return Mage_Paypal_Model_Express_Checkout
933:      */
934:     protected function _prepareCustomerQuote()
935:     {
936:         $quote      = $this->_quote;
937:         $billing    = $quote->getBillingAddress();
938:         $shipping   = $quote->isVirtual() ? null : $quote->getShippingAddress();
939: 
940:         $customer = $this->getCustomerSession()->getCustomer();
941:         if (!$billing->getCustomerId() || $billing->getSaveInAddressBook()) {
942:             $customerBilling = $billing->exportCustomerAddress();
943:             $customer->addAddress($customerBilling);
944:             $billing->setCustomerAddress($customerBilling);
945:         }
946:         if ($shipping && ((!$shipping->getCustomerId() && !$shipping->getSameAsBilling())
947:             || (!$shipping->getSameAsBilling() && $shipping->getSaveInAddressBook()))) {
948:             $customerShipping = $shipping->exportCustomerAddress();
949:             $customer->addAddress($customerShipping);
950:             $shipping->setCustomerAddress($customerShipping);
951:         }
952: 
953:         if (isset($customerBilling) && !$customer->getDefaultBilling()) {
954:             $customerBilling->setIsDefaultBilling(true);
955:         }
956:         if ($shipping && isset($customerBilling) && !$customer->getDefaultShipping() && $shipping->getSameAsBilling()) {
957:             $customerBilling->setIsDefaultShipping(true);
958:         } elseif ($shipping && isset($customerShipping) && !$customer->getDefaultShipping()) {
959:             $customerShipping->setIsDefaultShipping(true);
960:         }
961:         $quote->setCustomer($customer);
962: 
963:         return $this;
964:     }
965: 
966:     /**
967:      * Involve new customer to system
968:      *
969:      * @return Mage_Paypal_Model_Express_Checkout
970:      */
971:     protected function _involveNewCustomer()
972:     {
973:         $customer = $this->_quote->getCustomer();
974:         if ($customer->isConfirmationRequired()) {
975:             $customer->sendNewAccountEmail('confirmation');
976:             $url = Mage::helper('customer')->getEmailConfirmationUrl($customer->getEmail());
977:             $this->getCustomerSession()->addSuccess(
978:                 Mage::helper('customer')->__('Account confirmation is required. Please, check your e-mail for confirmation link. To resend confirmation email please <a href="%s">click here</a>.', $url)
979:             );
980:         } else {
981:             $customer->sendNewAccountEmail();
982:             $this->getCustomerSession()->loginById($customer->getId());
983:         }
984:         return $this;
985:     }
986: 
987:     /**
988:      * Get customer session object
989:      *
990:      * @return Mage_Customer_Model_Session
991:      */
992:     public function getCustomerSession()
993:     {
994:         return $this->_customerSession;
995:     }
996: }
997: 
Magento 1.7.0.2 API documentation generated by ApiGen 2.8.0