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: 32: 33:
34:
35: class Mage_Paypal_Model_Payflowlink extends Mage_Paypal_Model_Payflowpro
36: {
37: 38: 39:
40: const LAYOUT_TEMPLATE = 'minLayout';
41:
42: 43: 44: 45: 46:
47: protected $_callbackController = 'payflow';
48:
49: 50: 51: 52: 53:
54: protected $_responseParamsMappings = array(
55: 'firstname' => 'billtofirstname',
56: 'lastname' => 'billtolastname',
57: 'address' => 'billtostreet',
58: 'city' => 'billtocity',
59: 'state' => 'billtostate',
60: 'zip' => 'billtozip',
61: 'country' => 'billtocountry',
62: 'phone' => 'billtophone',
63: 'email' => 'billtoemail',
64: 'nametoship' => 'shiptofirstname',
65: 'addresstoship' => 'shiptostreet',
66: 'citytoship' => 'shiptocity',
67: 'statetoship' => 'shiptostate',
68: 'ziptoship' => 'shiptozip',
69: 'countrytoship' => 'shiptocountry',
70: 'phonetoship' => 'shiptophone',
71: 'emailtoship' => 'shiptoemail',
72: 'faxtoship' => 'shiptofax',
73: 'method' => 'tender',
74: 'cscmatch' => 'cvv2match',
75: 'type' => 'trxtype',
76: );
77:
78: 79: 80:
81: protected $_code = Mage_Paypal_Model_Config::METHOD_PAYFLOWLINK;
82:
83: protected $_formBlockType = 'paypal/payflow_link_form';
84: protected $_infoBlockType = 'paypal/payflow_link_info';
85:
86: 87: 88:
89: protected $_canUseInternal = false;
90: protected $_canUseForMultishipping = false;
91: protected $_isInitializeNeeded = true;
92:
93: 94: 95: 96:
97: protected $_response;
98:
99: 100: 101: 102:
103: const TRANSACTION_PAYFLOW_URL = 'https://payflowlink.paypal.com/';
104:
105: 106: 107: 108:
109: const RESPONSE_ERROR_MSG = 'Payment error. %s was not found.';
110:
111: 112: 113: 114: 115:
116: protected $_secureSilentPostHashKey = 'secure_silent_post_hash';
117:
118: 119: 120: 121: 122:
123: public function validate()
124: {
125: return true;
126: }
127:
128: 129: 130: 131: 132: 133:
134: public function isAvailable($quote = null)
135: {
136: $storeId = Mage::app()->getStore($this->getStore())->getId();
137: $config = Mage::getModel('paypal/config')->setStoreId($storeId);
138: if (Mage_Payment_Model_Method_Abstract::isAvailable($quote) && $config->isMethodAvailable($this->getCode())) {
139: return true;
140: }
141: return false;
142: }
143:
144: 145: 146: 147: 148: 149:
150: public function initialize($paymentAction, $stateObject)
151: {
152: switch ($paymentAction) {
153: case Mage_Paypal_Model_Config::PAYMENT_ACTION_AUTH:
154: case Mage_Paypal_Model_Config::PAYMENT_ACTION_SALE:
155: $payment = $this->getInfoInstance();
156: $order = $payment->getOrder();
157: $order->setCanSendNewEmailFlag(false);
158: $payment->setAmountAuthorized($order->getTotalDue());
159: $payment->setBaseAmountAuthorized($order->getBaseTotalDue());
160: $this->_generateSecureSilentPostHash($payment);
161: $request = $this->_buildTokenRequest($payment);
162: $response = $this->_postRequest($request);
163: $this->_processTokenErrors($response, $payment);
164:
165: $order = $payment->getOrder();
166: $order->setCanSendNewEmailFlag(false);
167:
168: $stateObject->setState(Mage_Sales_Model_Order::STATE_PENDING_PAYMENT);
169: $stateObject->setStatus('pending_payment');
170: $stateObject->setIsNotified(false);
171: break;
172: default:
173: break;
174: }
175: }
176:
177: 178: 179: 180: 181:
182: public function getResponse()
183: {
184: if (!$this->_response) {
185: $this->_response = Mage::getModel('paypal/payflow_request');
186: }
187:
188: return $this->_response;
189: }
190:
191: 192: 193: 194: 195: 196:
197: public function setResponseData(array $postData)
198: {
199: foreach ($postData as $key => $val) {
200: $this->getResponse()->setData(strtolower($key), $val);
201: }
202: foreach ($this->_responseParamsMappings as $originKey => $key) {
203: $data = $this->getResponse()->getData($key);
204: if (isset($data)) {
205: $this->getResponse()->setData($originKey, $data);
206: }
207: }
208:
209: $avsAddr = $this->getResponse()->getData('avsaddr');
210: $avsZip = $this->getResponse()->getData('avszip');
211: if (isset($avsAddr) && isset($avsZip)) {
212: $this->getResponse()->setData('avsdata', $avsAddr . $avsZip);
213: }
214:
215: $firstnameParameter = $this->getResponse()->getData('billtofirstname');
216: $lastnameParameter = $this->getResponse()->getData('billtolastname');
217: if (isset($firstnameParameter) && isset($lastnameParameter)) {
218: $this->getResponse()->setData('name', $firstnameParameter . ' ' . $lastnameParameter);
219: }
220:
221: return $this;
222: }
223:
224: 225: 226: 227: 228: 229:
230: public function process($responseData)
231: {
232: $debugData = array(
233: 'response' => $responseData
234: );
235: $this->_debug($debugData);
236:
237: $this->setResponseData($responseData);
238: $order = $this->_getOrderFromResponse();
239:
240: if ($order) {
241: $this->_processOrder($order);
242: }
243: }
244:
245: 246: 247: 248: 249:
250: protected function _processOrder(Mage_Sales_Model_Order $order)
251: {
252: $response = $this->getResponse();
253: $payment = $order->getPayment();
254: $payment->setTransactionId($response->getPnref())
255: ->setIsTransactionClosed(0);
256: $canSendNewOrderEmail = true;
257:
258: if ($response->getResult() == self::RESPONSE_CODE_FRAUDSERVICE_FILTER ||
259: $response->getResult() == self::RESPONSE_CODE_DECLINED_BY_FILTER
260: ) {
261: $canSendNewOrderEmail = false;
262: $fraudMessage = $this->_getFraudMessage() ?
263: $response->getFraudMessage() : $response->getRespmsg();
264: $payment->setIsTransactionPending(true)
265: ->setIsFraudDetected(true)
266: ->setAdditionalInformation('paypal_fraud_filters', $fraudMessage);
267: }
268:
269: if ($response->getAvsdata() && strstr(substr($response->getAvsdata(), 0, 2), 'N')) {
270: $payment->setAdditionalInformation('paypal_avs_code', substr($response->getAvsdata(), 0, 2));
271: }
272: if ($response->getCvv2match() && $response->getCvv2match() != 'Y') {
273: $payment->setAdditionalInformation('paypal_cvv2_match', $response->getCvv2match());
274: }
275:
276: switch ($response->getType()){
277: case self::TRXTYPE_AUTH_ONLY:
278: $payment->registerAuthorizationNotification($payment->getBaseAmountAuthorized());
279: break;
280: case self::TRXTYPE_SALE:
281: $payment->registerCaptureNotification($payment->getBaseAmountAuthorized());
282: break;
283: }
284: $order->save();
285:
286: try {
287: if ($canSendNewOrderEmail) {
288: $order->sendNewOrderEmail();
289: }
290: Mage::getModel('sales/quote')
291: ->load($order->getQuoteId())
292: ->setIsActive(false)
293: ->save();
294: } catch (Exception $e) {
295: Mage::throwException(Mage::helper('paypal')->__('Can not send new order email.'));
296: }
297: }
298:
299: 300: 301: 302: 303:
304: protected function _getFraudMessage()
305: {
306: if ($this->getResponse()->getFpsPrexmldata()) {
307: $xml = new SimpleXMLElement($this->getResponse()->getFpsPrexmldata());
308: $this->getResponse()->setFraudMessage((string) $xml->rule->triggeredMessage);
309: return $this->getResponse()->getFraudMessage();
310: }
311:
312: return false;
313: }
314:
315: 316: 317: 318: 319: 320:
321: protected function _getOrderFromResponse()
322: {
323: $response = $this->getResponse();
324:
325: $order = Mage::getModel('sales/order')
326: ->loadByIncrementId($response->getInvnum());
327:
328: if ($this->_getSecureSilentPostHash($order->getPayment()) != $response->getUser2()
329: || $this->_code != $order->getPayment()->getMethodInstance()->getCode()
330: ) {
331: return false;
332: }
333:
334: if ($response->getResult() != self::RESPONSE_CODE_FRAUDSERVICE_FILTER
335: && $response->getResult() != self::RESPONSE_CODE_DECLINED_BY_FILTER
336: && $response->getResult() != self::RESPONSE_CODE_APPROVED
337: ) {
338: if ($order->getState() != Mage_Sales_Model_Order::STATE_CANCELED) {
339: $order->registerCancellation($response->getRespmsg())->save();
340: }
341: Mage::throwException($response->getRespmsg());
342: }
343:
344: $amountCompared = ($response->getAmt() == $order->getPayment()->getBaseAmountAuthorized()) ? true : false;
345: if (!$order->getId()
346: || $order->getState() != Mage_Sales_Model_Order::STATE_PENDING_PAYMENT
347: || !$amountCompared
348: ) {
349: Mage::throwException($this->_formatStr(self::RESPONSE_ERROR_MSG, 'Order'));
350: }
351:
352: $fetchData = $this->fetchTransactionInfo($order->getPayment(), $response->getPnref());
353: if (!isset($fetchData['custref']) || $fetchData['custref'] != $order->getIncrementId()) {
354: Mage::throwException($this->_formatStr(self::RESPONSE_ERROR_MSG, 'Transaction'));
355: }
356:
357: return $order;
358: }
359:
360: 361: 362: 363: 364: 365:
366: protected function _buildTokenRequest(Mage_Sales_Model_Order_Payment $payment)
367: {
368: $request = $this->_buildBasicRequest($payment);
369: $request->setCreatesecuretoken('Y')
370: ->setSecuretokenid($this->_generateSecureTokenId())
371: ->setTrxtype($this->_getTrxTokenType())
372: ->setAmt($this->_formatStr('%.2F', $payment->getOrder()->getBaseTotalDue()))
373: ->setCurrency($payment->getOrder()->getBaseCurrencyCode())
374: ->setInvnum($payment->getOrder()->getIncrementId())
375: ->setCustref($payment->getOrder()->getIncrementId())
376: ->setPonum($payment->getOrder()->getId());
377:
378:
379:
380:
381:
382:
383: $order = $payment->getOrder();
384: if (empty($order)) {
385: return $request;
386: }
387:
388: $billing = $order->getBillingAddress();
389: if (!empty($billing)) {
390: $request->setFirstname($billing->getFirstname())
391: ->setLastname($billing->getLastname())
392: ->setStreet(implode(' ', $billing->getStreet()))
393: ->setCity($billing->getCity())
394: ->setState($billing->getRegionCode())
395: ->setZip($billing->getPostcode())
396: ->setCountry($billing->getCountry())
397: ->setEmail($order->getCustomerEmail());
398: }
399: $shipping = $order->getShippingAddress();
400: if (!empty($shipping)) {
401: $this->_applyCountryWorkarounds($shipping);
402: $request->setShiptofirstname($shipping->getFirstname())
403: ->setShiptolastname($shipping->getLastname())
404: ->setShiptostreet(implode(' ', $shipping->getStreet()))
405: ->setShiptocity($shipping->getCity())
406: ->setShiptostate($shipping->getRegionCode())
407: ->setShiptozip($shipping->getPostcode())
408: ->setShiptocountry($shipping->getCountry());
409: }
410:
411: $request->setUser1($order->getStoreId())
412: ->setUser2($this->_getSecureSilentPostHash($payment));
413:
414: return $request;
415: }
416:
417: 418: 419: 420: 421: 422:
423: protected function _getStoreId()
424: {
425: $response = $this->getResponse();
426: if ($response->getUser1()) {
427: return (int) $response->getUser1();
428: }
429:
430: return Mage::app()->getStore($this->getStore())->getId();
431: }
432:
433: 434: 435: 436: 437: 438:
439: protected function _buildBasicRequest(Varien_Object $payment)
440: {
441: $request = Mage::getModel('paypal/payflow_request');
442: $cscEditable = $this->getConfigData('csc_editable');
443: $request
444: ->setUser($this->getConfigData('user', $this->_getStoreId()))
445: ->setVendor($this->getConfigData('vendor', $this->_getStoreId()))
446: ->setPartner($this->getConfigData('partner', $this->_getStoreId()))
447: ->setPwd($this->getConfigData('pwd', $this->_getStoreId()))
448: ->setVerbosity($this->getConfigData('verbosity', $this->_getStoreId()))
449: ->setTender(self::TENDER_CC)
450: ->setCancelurl($this->_getCallbackUrl('cancelPayment'))
451: ->setErrorurl($this->_getCallbackUrl('returnUrl'))
452: ->setSilentpost('TRUE')
453: ->setSilentposturl($this->_getCallbackUrl('silentPost'))
454: ->setReturnurl($this->_getCallbackUrl('returnUrl'))
455: ->setTemplate(self::LAYOUT_TEMPLATE)
456: ->setDisablereceipt('TRUE')
457: ->setCscrequired($cscEditable && $this->getConfigData('csc_required') ? 'TRUE' : 'FALSE')
458: ->setCscedit($cscEditable ? 'TRUE' : 'FALSE')
459: ->setEmailcustomer($this->getConfigData('email_confirmation') ? 'TRUE' : 'FALSE')
460: ->setUrlmethod($this->getConfigData('url_method'));
461: return $request;
462: }
463:
464: 465: 466: 467: 468:
469: protected function _getTrxTokenType()
470: {
471: switch ($this->getConfigData('payment_action')) {
472: case Mage_Paypal_Model_Config::PAYMENT_ACTION_AUTH:
473: return self::TRXTYPE_AUTH_ONLY;
474: case Mage_Paypal_Model_Config::PAYMENT_ACTION_SALE:
475: return self::TRXTYPE_SALE;
476: }
477: }
478:
479: 480: 481: 482: 483:
484: protected function _generateSecureTokenId()
485: {
486: return Mage::helper('core')->uniqHash();
487: }
488:
489: 490: 491: 492: 493: 494: 495:
496: protected function _formatStr($format, $string)
497: {
498: return sprintf($format, $string);
499: }
500:
501: 502: 503: 504: 505: 506: 507: 508:
509: protected function _processTokenErrors($response, $payment)
510: {
511: if (!$response->getSecuretoken() &&
512: $response->getResult() != self::RESPONSE_CODE_APPROVED
513: && $response->getResult() != self::RESPONSE_CODE_FRAUDSERVICE_FILTER) {
514: Mage::throwException($response->getRespmsg());
515: } else {
516: $payment->setAdditionalInformation('secure_token_id', $response->getSecuretokenid())
517: ->setAdditionalInformation('secure_token', $response->getSecuretoken());
518: }
519: }
520:
521: 522: 523: 524: 525: 526:
527: protected function _getSecureSilentPostHash($payment)
528: {
529: return $payment->getAdditionalInformation($this->_secureSilentPostHashKey);
530: }
531:
532: 533: 534: 535: 536: 537:
538: protected function _generateSecureSilentPostHash($payment)
539: {
540: $secureHash = md5(Mage::helper('core')->getRandomString(10));
541: $payment->setAdditionalInformation($this->_secureSilentPostHashKey, $secureHash);
542: return $secureHash;
543: }
544:
545: 546: 547: 548: 549: 550: 551: 552:
553: protected function _addTransaction($payment, $txnId)
554: {
555: }
556:
557: 558: 559: 560: 561: 562: 563: 564:
565: protected function _initialize(Varien_Object $payment, $amount)
566: {
567: return $this;
568: }
569:
570: 571: 572: 573: 574: 575: 576:
577: public function prepareOrderReview($token = null)
578: {
579: }
580:
581: 582: 583: 584: 585: 586: 587: 588: 589: 590:
591: protected function _authorize(Varien_Object $payment, $amount, $transaction, $txnId)
592: {
593: return $this;
594: }
595:
596: 597: 598: 599: 600: 601:
602: protected function _process(Varien_Object $document)
603: {
604: }
605:
606: 607: 608: 609: 610: 611: 612: 613:
614: protected function _checkTransaction($transaction, $amount)
615: {
616: return $this;
617: }
618:
619: 620: 621: 622: 623: 624: 625:
626: protected function _getDocumentFromResponse()
627: {
628: return null;
629: }
630:
631: 632: 633: 634: 635:
636: public function getCallbackController()
637: {
638: return $this->_callbackController;
639: }
640:
641: 642: 643: 644: 645: 646:
647: protected function _getCallbackUrl($actionName)
648: {
649: $request = Mage::app()->getRequest();
650: if ($request->getParam('website')) {
651: $website = Mage::getModel('core/website')->load($request->getParam('website'));
652: $secure = Mage::getStoreConfigFlag(
653: Mage_Core_Model_Url::XML_PATH_SECURE_IN_FRONT,
654: $website->getDefaultStore()
655: );
656: $path = $secure
657: ? Mage_Core_Model_Store::XML_PATH_SECURE_BASE_LINK_URL
658: : Mage_Core_Model_Store::XML_PATH_UNSECURE_BASE_LINK_URL;
659: $websiteUrl = Mage::getStoreConfig($path, $website->getDefaultStore());
660: } else {
661: $secure = Mage::getStoreConfigFlag(Mage_Core_Model_Url::XML_PATH_SECURE_IN_FRONT);
662: $websiteUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK, $secure);
663: }
664:
665: return $websiteUrl . 'paypal/' . $this->getCallbackController() . '/' . $actionName;
666: }
667: }
668: