1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27: 28: 29: 30:
31: class Mage_Paypal_Model_Express extends Mage_Payment_Model_Method_Abstract
32: implements Mage_Payment_Model_Recurring_Profile_MethodInterface
33: {
34: protected $_code = Mage_Paypal_Model_Config::METHOD_WPP_EXPRESS;
35: protected $_formBlockType = 'paypal/express_form';
36: protected $_infoBlockType = 'paypal/payment_info';
37:
38: 39: 40: 41: 42:
43: protected $_proType = 'paypal/pro';
44:
45: 46: 47:
48: protected $_isGateway = false;
49: protected $_canOrder = true;
50: protected $_canAuthorize = true;
51: protected $_canCapture = true;
52: protected $_canCapturePartial = true;
53: protected $_canRefund = true;
54: protected $_canRefundInvoicePartial = true;
55: protected $_canVoid = true;
56: protected $_canUseInternal = false;
57: protected $_canUseCheckout = true;
58: protected $_canUseForMultishipping = false;
59: protected $_canFetchTransactionInfo = true;
60: protected $_canCreateBillingAgreement = true;
61: protected $_canReviewPayment = true;
62:
63: 64: 65: 66: 67:
68: protected $_pro = null;
69:
70: 71: 72: 73:
74: protected $_isOrderPaymentActionKey = 'is_order_action';
75:
76: 77: 78: 79:
80: protected $_authorizationCountKey = 'authorization_count';
81:
82: public function __construct($params = array())
83: {
84: $proInstance = array_shift($params);
85: if ($proInstance && ($proInstance instanceof Mage_Paypal_Model_Pro)) {
86: $this->_pro = $proInstance;
87: } else {
88: $this->_pro = Mage::getModel($this->_proType);
89: }
90: $this->_pro->setMethod($this->_code);
91: }
92:
93: 94: 95: 96: 97: 98:
99: public function setStore($store)
100: {
101: $this->setData('store', $store);
102: if (null === $store) {
103: $store = Mage::app()->getStore()->getId();
104: }
105: $this->_pro->getConfig()->setStoreId(is_object($store) ? $store->getId() : $store);
106: return $this;
107: }
108:
109: 110: 111: 112: 113:
114: public function canUseCheckout()
115: {
116: if (Mage::getStoreConfigFlag('payment/hosted_pro/active')
117: && !Mage::getStoreConfigFlag('payment/hosted_pro/display_ec')
118: ) {
119: return false;
120: }
121: return parent::canUseCheckout();
122: }
123:
124: 125: 126: 127: 128: 129:
130: public function canUseForCurrency($currencyCode)
131: {
132: return $this->_pro->getConfig()->isCurrencyCodeSupported($currencyCode);
133: }
134:
135: 136: 137: 138: 139: 140:
141: public function getConfigPaymentAction()
142: {
143: return $this->_pro->getConfig()->getPaymentAction();
144: }
145:
146: 147: 148: 149: 150:
151: public function isAvailable($quote = null)
152: {
153: if (parent::isAvailable($quote) && $this->_pro->getConfig()->isMethodAvailable()) {
154: return true;
155: }
156: return false;
157: }
158:
159: 160: 161: 162: 163: 164: 165:
166: public function getConfigData($field, $storeId = null)
167: {
168: return $this->_pro->getConfig()->$field;
169: }
170:
171: 172: 173: 174: 175: 176: 177:
178: public function order(Varien_Object $payment, $amount)
179: {
180: $this->_placeOrder($payment, $amount);
181:
182: $payment->setAdditionalInformation($this->_isOrderPaymentActionKey, true);
183:
184: if ($payment->getIsFraudDetected()) {
185: return $this;
186: }
187:
188: $order = $payment->getOrder();
189: $orderTransactionId = $payment->getTransactionId();
190:
191: $api = $this->_callDoAuthorize($amount, $payment, $payment->getTransactionId());
192:
193: $state = Mage_Sales_Model_Order::STATE_PROCESSING;
194: $status = true;
195:
196: $formatedPrice = $order->getBaseCurrency()->formatTxt($amount);
197: if ($payment->getIsTransactionPending()) {
198: $message = Mage::helper('paypal')->__('Ordering amount of %s is pending approval on gateway.', $formatedPrice);
199: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
200: } else {
201: $message = Mage::helper('paypal')->__('Ordered amount of %s.', $formatedPrice);
202: }
203:
204: $payment->addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER, null, false, $message);
205:
206: $this->_pro->importPaymentInfo($api, $payment);
207:
208: if ($payment->getIsTransactionPending()) {
209: $message = Mage::helper('paypal')->__('Authorizing amount of %s is pending approval on gateway.', $formatedPrice);
210: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
211: if ($payment->getIsFraudDetected()) {
212: $status = Mage_Sales_Model_Order::STATUS_FRAUD;
213: }
214: } else {
215: $message = Mage::helper('paypal')->__('Authorized amount of %s.', $formatedPrice);
216: }
217:
218: $payment->resetTransactionAdditionalInfo();
219:
220: $payment->setTransactionId($api->getTransactionId());
221: $payment->setParentTransactionId($orderTransactionId);
222:
223: $transaction = $payment->addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH, null, false,
224: $message
225: );
226:
227: $order->setState($state, $status);
228:
229: $payment->setSkipOrderProcessing(true);
230: return $this;
231: }
232:
233: 234: 235: 236: 237: 238: 239:
240: public function authorize(Varien_Object $payment, $amount)
241: {
242: return $this->_placeOrder($payment, $amount);
243: }
244:
245: 246: 247: 248: 249: 250:
251: public function void(Varien_Object $payment)
252: {
253:
254: if ($payment->getAdditionalInformation($this->_isOrderPaymentActionKey)
255: && !$payment->getVoidOnlyAuthorization()
256: ) {
257: $orderTransaction = $payment->lookupTransaction(
258: false, Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER
259: );
260: if ($orderTransaction) {
261: $payment->setParentTransactionId($orderTransaction->getTxnId());
262: $payment->setTransactionId($orderTransaction->getTxnId() . '-void');
263: }
264: }
265: $this->_pro->void($payment);
266: return $this;
267: }
268:
269: 270: 271: 272: 273: 274: 275:
276: public function capture(Varien_Object $payment, $amount)
277: {
278: $authorizationTransaction = $payment->getAuthorizationTransaction();
279: $authorizationPeriod = abs(intval($this->getConfigData('authorization_honor_period')));
280: $maxAuthorizationNumber = abs(intval($this->getConfigData('child_authorization_number')));
281: $order = $payment->getOrder();
282: $isAuthorizationCreated = false;
283:
284: if ($payment->getAdditionalInformation($this->_isOrderPaymentActionKey)) {
285: $voided = false;
286: if (!$authorizationTransaction->getIsClosed()
287: && $this->_isTransactionExpired($authorizationTransaction, $authorizationPeriod)
288: ) {
289:
290: $isCaptureFinal = $payment->getShouldCloseParentTransaction();
291: $captureTrxId = $payment->getTransactionId();
292: $payment->setShouldCloseParentTransaction(false);
293: $payment->setParentTransactionId($authorizationTransaction->getTxnId());
294: $payment->unsTransactionId();
295: $payment->setVoidOnlyAuthorization(true);
296: $payment->void(new Varien_Object());
297:
298:
299: $payment->unsAuthorizationTransaction();
300: $payment->unsTransactionId();
301: $payment->setShouldCloseParentTransaction($isCaptureFinal);
302: $voided = true;
303: }
304:
305: if ($authorizationTransaction->getIsClosed() || $voided) {
306: if ($payment->getAdditionalInformation($this->_authorizationCountKey) > $maxAuthorizationNumber - 1) {
307: Mage::throwException(Mage::helper('paypal')->__('The maximum number of child authorizations is reached.'));
308: }
309: $api = $this->_callDoAuthorize(
310: $amount,
311: $payment,
312: $authorizationTransaction->getParentTxnId()
313: );
314:
315:
316: $this->_pro->importPaymentInfo($api, $payment);
317: $payment->setTransactionId($api->getTransactionId());
318: $payment->setParentTransactionId($authorizationTransaction->getParentTxnId());
319: $payment->setIsTransactionClosed(false);
320:
321: $formatedPrice = $order->getBaseCurrency()->formatTxt($amount);
322:
323: if ($payment->getIsTransactionPending()) {
324: $message = Mage::helper('paypal')->__('Authorizing amount of %s is pending approval on gateway.', $formatedPrice);
325: } else {
326: $message = Mage::helper('paypal')->__('Authorized amount of %s.', $formatedPrice);
327: }
328:
329: $transaction = $payment->addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH, null,
330: true, $message
331: );
332:
333: $payment->setParentTransactionId($api->getTransactionId());
334: $isAuthorizationCreated = true;
335: }
336:
337: if ($payment->getShouldCloseParentTransaction()) {
338: $orderTransaction = $payment->lookupTransaction(
339: false, Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER
340: );
341:
342: if ($orderTransaction) {
343: $orderTransaction->setIsClosed(true);
344: $order->addRelatedObject($orderTransaction);
345: }
346: }
347: }
348:
349: if (false === $this->_pro->capture($payment, $amount)) {
350: $this->_placeOrder($payment, $amount);
351: }
352:
353: if ($isAuthorizationCreated && isset($transaction)) {
354: $transaction->setIsClosed(true);
355: }
356:
357: return $this;
358: }
359:
360: 361: 362: 363: 364: 365: 366:
367: public function refund(Varien_Object $payment, $amount)
368: {
369: $this->_pro->refund($payment, $amount);
370: return $this;
371: }
372:
373: 374: 375: 376: 377: 378:
379: public function cancel(Varien_Object $payment)
380: {
381: $this->void($payment);
382:
383: return $this;
384: }
385:
386: 387: 388: 389: 390: 391:
392: public function canReviewPayment(Mage_Payment_Model_Info $payment)
393: {
394: return parent::canReviewPayment($payment) && $this->_pro->canReviewPayment($payment);
395: }
396:
397: 398: 399: 400: 401: 402:
403: public function acceptPayment(Mage_Payment_Model_Info $payment)
404: {
405: parent::acceptPayment($payment);
406: return $this->_pro->reviewPayment($payment, Mage_Paypal_Model_Pro::PAYMENT_REVIEW_ACCEPT);
407: }
408:
409: 410: 411: 412: 413: 414:
415: public function denyPayment(Mage_Payment_Model_Info $payment)
416: {
417: parent::denyPayment($payment);
418: return $this->_pro->reviewPayment($payment, Mage_Paypal_Model_Pro::PAYMENT_REVIEW_DENY);
419: }
420:
421: 422: 423: 424: 425: 426: 427:
428: public function getCheckoutRedirectUrl()
429: {
430: return Mage::getUrl('paypal/express/start');
431: }
432:
433: 434: 435: 436: 437: 438: 439:
440: public function fetchTransactionInfo(Mage_Payment_Model_Info $payment, $transactionId)
441: {
442: return $this->_pro->fetchTransactionInfo($payment, $transactionId);
443: }
444:
445: 446: 447: 448: 449:
450: public function validateRecurringProfile(Mage_Payment_Model_Recurring_Profile $profile)
451: {
452: return $this->_pro->validateRecurringProfile($profile);
453: }
454:
455: 456: 457: 458: 459: 460:
461: public function submitRecurringProfile(Mage_Payment_Model_Recurring_Profile $profile,
462: Mage_Payment_Model_Info $paymentInfo
463: ) {
464: $token = $paymentInfo->
465: getAdditionalInformation(Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_TOKEN);
466: $profile->setToken($token);
467: $this->_pro->submitRecurringProfile($profile, $paymentInfo);
468: }
469:
470: 471: 472: 473: 474: 475:
476: public function getRecurringProfileDetails($referenceId, Varien_Object $result)
477: {
478: return $this->_pro->getRecurringProfileDetails($referenceId, $result);
479: }
480:
481: 482: 483:
484: public function canGetRecurringProfileDetails()
485: {
486: return true;
487: }
488:
489: 490: 491: 492: 493:
494: public function updateRecurringProfile(Mage_Payment_Model_Recurring_Profile $profile)
495: {
496: return $this->_pro->updateRecurringProfile($profile);
497: }
498:
499: 500: 501: 502: 503:
504: public function updateRecurringProfileStatus(Mage_Payment_Model_Recurring_Profile $profile)
505: {
506: return $this->_pro->updateRecurringProfileStatus($profile);
507: }
508:
509: 510: 511: 512: 513: 514:
515: public function assignData($data)
516: {
517: $result = parent::assignData($data);
518: $key = Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_BILLING_AGREEMENT;
519: if (is_array($data)) {
520: $this->getInfoInstance()->setAdditionalInformation($key, isset($data[$key]) ? $data[$key] : null);
521: }
522: elseif ($data instanceof Varien_Object) {
523: $this->getInfoInstance()->setAdditionalInformation($key, $data->getData($key));
524: }
525: return $result;
526: }
527:
528: 529: 530: 531: 532: 533: 534:
535: protected function _placeOrder(Mage_Sales_Model_Order_Payment $payment, $amount)
536: {
537: $order = $payment->getOrder();
538:
539:
540: $token = $payment->getAdditionalInformation(Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_TOKEN);
541: $api = $this->_pro->getApi()
542: ->setToken($token)
543: ->setPayerId($payment->
544: getAdditionalInformation(Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_PAYER_ID))
545: ->setAmount($amount)
546: ->setPaymentAction($this->_pro->getConfig()->paymentAction)
547: ->setNotifyUrl(Mage::getUrl('paypal/ipn/'))
548: ->setInvNum($order->getIncrementId())
549: ->setCurrencyCode($order->getBaseCurrencyCode())
550: ->setPaypalCart(Mage::getModel('paypal/cart', array($order)))
551: ->setIsLineItemsEnabled($this->_pro->getConfig()->lineItemsEnabled)
552: ;
553: if ($order->getIsVirtual()) {
554: $api->setAddress($order->getBillingAddress())->setSuppressShipping(true);
555: } else {
556: $api->setAddress($order->getShippingAddress());
557: $api->setBillingAddress($order->getBillingAddress());
558: }
559:
560:
561: $api->callDoExpressCheckoutPayment();
562:
563: $this->_importToPayment($api, $payment);
564: return $this;
565: }
566:
567: 568: 569: 570: 571: 572:
573: protected function _importToPayment($api, $payment)
574: {
575: $payment->setTransactionId($api->getTransactionId())->setIsTransactionClosed(0)
576: ->setAdditionalInformation(Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_REDIRECT,
577: $api->getRedirectRequired()
578: );
579:
580: if ($api->getBillingAgreementId()) {
581: $payment->setBillingAgreementData(array(
582: 'billing_agreement_id' => $api->getBillingAgreementId(),
583: 'method_code' => Mage_Paypal_Model_Config::METHOD_BILLING_AGREEMENT
584: ));
585: }
586:
587: $this->_pro->importPaymentInfo($api, $payment);
588: }
589:
590: 591: 592: 593: 594: 595:
596: public function canVoid(Varien_Object $payment)
597: {
598: if ($payment instanceof Mage_Sales_Model_Order_Invoice
599: || $payment instanceof Mage_Sales_Model_Order_Creditmemo
600: ) {
601: return false;
602: }
603: $info = $this->getInfoInstance();
604: if ($info->getAdditionalInformation($this->_isOrderPaymentActionKey)) {
605: $orderTransaction = $info->lookupTransaction(
606: false, Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER
607: );
608: if ($orderTransaction) {
609: $info->setParentTransactionId($orderTransaction->getTxnId());
610: }
611: }
612:
613: return $this->_canVoid;
614: }
615:
616: 617: 618: 619: 620:
621: public function canCapture()
622: {
623: $payment = $this->getInfoInstance();
624: $this->_pro->getConfig()->setStoreId($payment->getOrder()->getStore()->getId());
625:
626: if ($payment->getAdditionalInformation($this->_isOrderPaymentActionKey)) {
627: $orderTransaction = $payment->lookupTransaction(false,
628: Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER
629: );
630: if ($orderTransaction->getIsClosed()) {
631: return false;
632: }
633:
634: $orderValidPeriod = abs(intval($this->getConfigData('order_valid_period')));
635:
636: $dateCompass = new DateTime($orderTransaction->getCreatedAt());
637: $dateCompass->modify('+' . $orderValidPeriod . ' days');
638: $currentDate = new DateTime();
639:
640: if ($currentDate > $dateCompass || $orderValidPeriod == 0) {
641: return false;
642: }
643: }
644: return $this->_canCapture;
645: }
646:
647: 648: 649: 650: 651: 652: 653: 654:
655: protected function _callDoAuthorize($amount, $payment, $parentTransactionId)
656: {
657: $api = $this->_pro->resetApi()->getApi()
658: ->setAmount($amount)
659: ->setCurrencyCode($payment->getOrder()->getBaseCurrencyCode())
660: ->setTransactionId($parentTransactionId)
661: ->callDoAuthorization();
662:
663: $payment->setAdditionalInformation($this->_authorizationCountKey,
664: $payment->getAdditionalInformation($this->_authorizationCountKey) + 1
665: );
666:
667: return $api;
668: }
669:
670: 671: 672: 673: 674: 675: 676:
677: protected function _isTransactionExpired(Mage_Sales_Model_Order_Payment_Transaction $transaction, $period)
678: {
679: $period = intval($period);
680: if (0 == $period) {
681: return true;
682: }
683:
684: $transactionClosingDate = new DateTime($transaction->getCreatedAt(), new DateTimeZone('GMT'));
685: $transactionClosingDate->setTimezone(new DateTimeZone('US/Pacific'));
686: 687: 688:
689: $transactionClosingDate->setTime(11, 49, 00);
690: $transactionClosingDate->modify('+' . $period . ' days');
691:
692: $currentTime = new DateTime(null, new DateTimeZone('US/Pacific'));
693:
694: if ($currentTime > $transactionClosingDate) {
695: return true;
696: }
697:
698: return false;
699: }
700: }
701: