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_Api_Nvp extends Mage_Paypal_Model_Api_Abstract
32: {
33: 34: 35:
36: const DO_DIRECT_PAYMENT = 'DoDirectPayment';
37: const DO_CAPTURE = 'DoCapture';
38: const DO_AUTHORIZATION = 'DoAuthorization';
39: const DO_VOID = 'DoVoid';
40: const REFUND_TRANSACTION = 'RefundTransaction';
41: const SET_EXPRESS_CHECKOUT = 'SetExpressCheckout';
42: const GET_EXPRESS_CHECKOUT_DETAILS = 'GetExpressCheckoutDetails';
43: const DO_EXPRESS_CHECKOUT_PAYMENT = 'DoExpressCheckoutPayment';
44: const CALLBACK_RESPONSE = 'CallbackResponse';
45:
46: 47: 48:
49: const PENDING_TRANSACTION_ACCEPT = 'Accept';
50: const PENDING_TRANSACTION_DENY = 'Deny';
51:
52: 53: 54: 55:
56: protected $_captureTypeComplete = 'Complete';
57: protected $_captureTypeNotcomplete = 'NotComplete';
58:
59: 60: 61: 62:
63: protected $_globalMap = array(
64:
65: 'VERSION' => 'version',
66: 'USER' => 'api_username',
67: 'PWD' => 'api_password',
68: 'SIGNATURE' => 'api_signature',
69: 'BUTTONSOURCE' => 'build_notation_code',
70:
71:
72: 'SUBJECT' => 'business_account',
73:
74:
75: 'PAYMENTACTION' => 'payment_action',
76: 'RETURNURL' => 'return_url',
77: 'CANCELURL' => 'cancel_url',
78: 'INVNUM' => 'inv_num',
79: 'TOKEN' => 'token',
80: 'CORRELATIONID' => 'correlation_id',
81: 'SOLUTIONTYPE' => 'solution_type',
82: 'GIROPAYCANCELURL' => 'giropay_cancel_url',
83: 'GIROPAYSUCCESSURL' => 'giropay_success_url',
84: 'BANKTXNPENDINGURL' => 'giropay_bank_txn_pending_url',
85: 'IPADDRESS' => 'ip_address',
86: 'NOTIFYURL' => 'notify_url',
87: 'RETURNFMFDETAILS' => 'fraud_management_filters_enabled',
88: 'NOTE' => 'note',
89: 'REFUNDTYPE' => 'refund_type',
90: 'ACTION' => 'action',
91: 'REDIRECTREQUIRED' => 'redirect_required',
92: 'SUCCESSPAGEREDIRECTREQUESTED' => 'redirect_requested',
93: 'REQBILLINGADDRESS' => 'require_billing_address',
94:
95: 'PAGESTYLE' => 'page_style',
96: 'HDRIMG' => 'hdrimg',
97: 'HDRBORDERCOLOR' => 'hdrbordercolor',
98: 'HDRBACKCOLOR' => 'hdrbackcolor',
99: 'PAYFLOWCOLOR' => 'payflowcolor',
100: 'LOCALECODE' => 'locale_code',
101: 'PAL' => 'pal',
102:
103:
104: 'TRANSACTIONID' => 'transaction_id',
105: 'AUTHORIZATIONID' => 'authorization_id',
106: 'REFUNDTRANSACTIONID' => 'refund_transaction_id',
107: 'COMPLETETYPE' => 'complete_type',
108: 'AMT' => 'amount',
109: 'ITEMAMT' => 'subtotal_amount',
110: 'GROSSREFUNDAMT' => 'refunded_amount',
111:
112:
113: 'CURRENCYCODE' => 'currency_code',
114: 'PAYMENTSTATUS' => 'payment_status',
115: 'PENDINGREASON' => 'pending_reason',
116: 'PROTECTIONELIGIBILITY' => 'protection_eligibility',
117: 'PAYERID' => 'payer_id',
118: 'PAYERSTATUS' => 'payer_status',
119: 'ADDRESSID' => 'address_id',
120: 'ADDRESSSTATUS' => 'address_status',
121: 'EMAIL' => 'email',
122:
123: 'FIRSTNAME' => 'firstname',
124: 'LASTNAME' => 'lastname',
125:
126:
127: 'SHIPPINGOPTIONNAME' => 'shipping_rate_code',
128: 'NOSHIPPING' => 'suppress_shipping',
129:
130:
131: 'CREDITCARDTYPE' => 'credit_card_type',
132: 'ACCT' => 'credit_card_number',
133: 'EXPDATE' => 'credit_card_expiration_date',
134: 'CVV2' => 'credit_card_cvv2',
135: 'STARTDATE' => 'maestro_solo_issue_date',
136: 'ISSUENUMBER' => 'maestro_solo_issue_number',
137: 'CVV2MATCH' => 'cvv2_check_result',
138: 'AVSCODE' => 'avs_result',
139:
140: 'AUTHSTATUS3DS' => 'centinel_authstatus',
141: 'MPIVENDOR3DS' => 'centinel_mpivendor',
142: 'CAVV' => 'centinel_cavv',
143: 'ECI3DS' => 'centinel_eci',
144: 'XID' => 'centinel_xid',
145: 'VPAS' => 'centinel_vpas_result',
146: 'ECISUBMITTED3DS' => 'centinel_eci_result',
147:
148:
149:
150: 'SUBSCRIBERNAME' =>'subscriber_name',
151: 'PROFILESTARTDATE' => 'start_datetime',
152: 'PROFILEREFERENCE' => 'internal_reference_id',
153: 'DESC' => 'schedule_description',
154: 'MAXFAILEDPAYMENTS' => 'suspension_threshold',
155: 'AUTOBILLAMT' => 'bill_failed_later',
156: 'BILLINGPERIOD' => 'period_unit',
157: 'BILLINGFREQUENCY' => 'period_frequency',
158: 'TOTALBILLINGCYCLES' => 'period_max_cycles',
159:
160: 'TRIALBILLINGPERIOD' => 'trial_period_unit',
161: 'TRIALBILLINGFREQUENCY' => 'trial_period_frequency',
162: 'TRIALTOTALBILLINGCYCLES' => 'trial_period_max_cycles',
163: 'TRIALAMT' => 'trial_billing_amount',
164:
165: 'SHIPPINGAMT' => 'shipping_amount',
166: 'TAXAMT' => 'tax_amount',
167: 'INITAMT' => 'init_amount',
168: 'FAILEDINITAMTACTION' => 'init_may_fail',
169: 'PROFILEID' => 'recurring_profile_id',
170: 'PROFILESTATUS' => 'recurring_profile_status',
171: 'STATUS' => 'status',
172:
173:
174: 'TAXID' => 'buyer_tax_id',
175: 'TAXIDTYPE' => 'buyer_tax_id_type',
176:
177: 'BILLINGAGREEMENTID' => 'billing_agreement_id',
178: 'REFERENCEID' => 'reference_id',
179: 'BILLINGAGREEMENTSTATUS' => 'billing_agreement_status',
180: 'BILLINGTYPE' => 'billing_type',
181: 'SREET' => 'street',
182: 'CITY' => 'city',
183: 'STATE' => 'state',
184: 'COUNTRYCODE' => 'countrycode',
185: 'ZIP' => 'zip',
186: 'PAYERBUSINESS' => 'payer_business',
187: );
188:
189: 190: 191: 192: 193:
194: protected $_exportToRequestFilters = array(
195: 'AMT' => '_filterAmount',
196: 'ITEMAMT' => '_filterAmount',
197: 'TRIALAMT' => '_filterAmount',
198: 'SHIPPINGAMT' => '_filterAmount',
199: 'TAXAMT' => '_filterAmount',
200: 'INITAMT' => '_filterAmount',
201: 'CREDITCARDTYPE' => '_filterCcType',
202:
203: 'AUTOBILLAMT' => '_filterBillFailedLater',
204: 'BILLINGPERIOD' => '_filterPeriodUnit',
205: 'TRIALBILLINGPERIOD' => '_filterPeriodUnit',
206: 'FAILEDINITAMTACTION' => '_filterInitialAmountMayFail',
207: 'BILLINGAGREEMENTSTATUS' => '_filterBillingAgreementStatus',
208: 'NOSHIPPING' => '_filterInt',
209: );
210:
211: protected $_importFromRequestFilters = array(
212: 'REDIRECTREQUIRED' => '_filterToBool',
213: 'SUCCESSPAGEREDIRECTREQUESTED' => '_filterToBool',
214: 'PAYMENTSTATUS' => '_filterPaymentStatusFromNvpToInfo',
215: );
216:
217: 218: 219: 220:
221: protected $_eachCallRequest = array('VERSION', 'USER', 'PWD', 'SIGNATURE', 'BUTTONSOURCE',);
222:
223: 224: 225: 226:
227: protected $_setExpressCheckoutRequest = array(
228: 'PAYMENTACTION', 'AMT', 'CURRENCYCODE', 'RETURNURL', 'CANCELURL', 'INVNUM', 'SOLUTIONTYPE', 'NOSHIPPING',
229: 'GIROPAYCANCELURL', 'GIROPAYSUCCESSURL', 'BANKTXNPENDINGURL',
230: 'PAGESTYLE', 'HDRIMG', 'HDRBORDERCOLOR', 'HDRBACKCOLOR', 'PAYFLOWCOLOR', 'LOCALECODE',
231: 'BILLINGTYPE', 'SUBJECT', 'ITEMAMT', 'SHIPPINGAMT', 'TAXAMT', 'REQBILLINGADDRESS',
232: );
233: protected $_setExpressCheckoutResponse = array('TOKEN');
234:
235: 236: 237: 238:
239: protected $_getExpressCheckoutDetailsRequest = array('TOKEN', 'SUBJECT',);
240:
241: 242: 243: 244:
245: protected $_doExpressCheckoutPaymentRequest = array(
246: 'TOKEN', 'PAYERID', 'PAYMENTACTION', 'AMT', 'CURRENCYCODE', 'IPADDRESS', 'BUTTONSOURCE', 'NOTIFYURL',
247: 'RETURNFMFDETAILS', 'SUBJECT', 'ITEMAMT', 'SHIPPINGAMT', 'TAXAMT',
248: );
249: protected $_doExpressCheckoutPaymentResponse = array(
250: 'TRANSACTIONID', 'AMT', 'PAYMENTSTATUS', 'PENDINGREASON', 'REDIRECTREQUIRED'
251: );
252:
253: 254: 255: 256:
257: protected $_doDirectPaymentRequest = array(
258: 'PAYMENTACTION', 'IPADDRESS', 'RETURNFMFDETAILS',
259: 'AMT', 'CURRENCYCODE', 'INVNUM', 'NOTIFYURL', 'EMAIL', 'ITEMAMT', 'SHIPPINGAMT', 'TAXAMT',
260: 'CREDITCARDTYPE', 'ACCT', 'EXPDATE', 'CVV2', 'STARTDATE', 'ISSUENUMBER',
261: 'AUTHSTATUS3DS', 'MPIVENDOR3DS', 'CAVV', 'ECI3DS', 'XID',
262: );
263: protected $_doDirectPaymentResponse = array(
264: 'TRANSACTIONID', 'AMT', 'AVSCODE', 'CVV2MATCH', 'VPAS', 'ECISUBMITTED3DS'
265: );
266:
267: 268: 269: 270:
271: protected $_doReauthorizationRequest = array('AUTHORIZATIONID', 'AMT', 'CURRENCYCODE');
272: protected $_doReauthorizationResponse = array(
273: 'AUTHORIZATIONID', 'PAYMENTSTATUS', 'PENDINGREASON', 'PROTECTIONELIGIBILITY'
274: );
275:
276: 277: 278: 279:
280: protected $_doCaptureRequest = array('AUTHORIZATIONID', 'COMPLETETYPE', 'AMT', 'CURRENCYCODE', 'NOTE', 'INVNUM',);
281: protected $_doCaptureResponse = array('TRANSACTIONID', 'CURRENCYCODE', 'AMT', 'PAYMENTSTATUS', 'PENDINGREASON',);
282:
283:
284: 285: 286: 287:
288: protected $_doAuthorizationRequest = array('TRANSACTIONID', 'AMT', 'CURRENCYCODE');
289: protected $_doAuthorizationResponse = array('TRANSACTIONID', 'AMT');
290:
291: 292: 293: 294:
295: protected $_doVoidRequest = array('AUTHORIZATIONID', 'NOTE',);
296:
297: 298: 299: 300:
301: protected $_getTransactionDetailsRequest = array('TRANSACTIONID');
302: protected $_getTransactionDetailsResponse = array(
303: 'PAYERID', 'FIRSTNAME', 'LASTNAME', 'TRANSACTIONID', 'PARENTTRANSACTIONID', 'CURRENCYCODE', 'AMT',
304: 'PAYMENTSTATUS', 'PENDINGREASON',
305: );
306:
307: 308: 309: 310:
311: protected $_refundTransactionRequest = array('TRANSACTIONID', 'REFUNDTYPE', 'CURRENCYCODE', 'NOTE',);
312: protected $_refundTransactionResponse = array('REFUNDTRANSACTIONID', 'GROSSREFUNDAMT',);
313:
314: 315: 316:
317: protected $_managePendingTransactionStatusRequest = array('TRANSACTIONID', 'ACTION');
318: protected $_managePendingTransactionStatusResponse = array('TRANSACTIONID', 'STATUS');
319:
320: 321: 322: 323:
324: protected $_getPalDetailsResponse = array('PAL');
325:
326: 327: 328: 329: 330:
331: protected $_createRecurringPaymentsProfileRequest = array(
332: 'TOKEN', 'SUBSCRIBERNAME', 'PROFILESTARTDATE', 'PROFILEREFERENCE', 'DESC', 'MAXFAILEDPAYMENTS', 'AUTOBILLAMT',
333: 'BILLINGPERIOD', 'BILLINGFREQUENCY', 'TOTALBILLINGCYCLES', 'AMT', 'TRIALBILLINGPERIOD', 'TRIALBILLINGFREQUENCY',
334: 'TRIALTOTALBILLINGCYCLES', 'TRIALAMT', 'CURRENCYCODE', 'SHIPPINGAMT', 'TAXAMT', 'INITAMT', 'FAILEDINITAMTACTION'
335: );
336: protected $_createRecurringPaymentsProfileResponse = array(
337: 'PROFILEID', 'PROFILESTATUS'
338: );
339:
340: 341: 342: 343: 344:
345: protected $_manageRecurringPaymentsProfileStatusRequest = array('PROFILEID', 'ACTION');
346:
347:
348: 349: 350: 351: 352:
353: protected $_getRecurringPaymentsProfileDetailsRequest = array('PROFILEID');
354: protected $_getRecurringPaymentsProfileDetailsResponse = array('STATUS', );
355:
356: 357: 358: 359:
360: protected $_billingAddressMap = array (
361: 'BUSINESS' => 'company',
362: 'NOTETEXT' => 'customer_notes',
363: 'EMAIL' => 'email',
364: 'FIRSTNAME' => 'firstname',
365: 'LASTNAME' => 'lastname',
366: 'MIDDLENAME' => 'middlename',
367: 'SALUTATION' => 'prefix',
368: 'SUFFIX' => 'suffix',
369:
370: 'COUNTRYCODE' => 'country_id',
371: 'STATE' => 'region',
372: 'CITY' => 'city',
373: 'STREET' => 'street',
374: 'STREET2' => 'street2',
375: 'ZIP' => 'postcode',
376: 'PHONENUM' => 'telephone',
377: );
378:
379: 380: 381: 382: 383: 384:
385: protected $_billingAddressMapRequest = array ();
386:
387: 388: 389: 390:
391: protected $_shippingAddressMap = array(
392: 'SHIPTOCOUNTRYCODE' => 'country_id',
393: 'SHIPTOSTATE' => 'region',
394: 'SHIPTOCITY' => 'city',
395: 'SHIPTOSTREET' => 'street',
396: 'SHIPTOSTREET2' => 'street2',
397: 'SHIPTOZIP' => 'postcode',
398: 'SHIPTOPHONENUM' => 'telephone',
399:
400: );
401:
402: 403: 404: 405:
406: protected $_callbackRequestMap = array(
407: 'SHIPTOCOUNTRY' => 'country_id',
408: 'SHIPTOSTATE' => 'region',
409: 'SHIPTOCITY' => 'city',
410: 'SHIPTOSTREET' => 'street',
411: 'SHIPTOSTREET2' => 'street2',
412: 'SHIPTOZIP' => 'postcode'
413: );
414:
415: 416: 417: 418:
419: protected $_paymentInformationResponse = array(
420: 'PAYERID', 'PAYERSTATUS', 'CORRELATIONID', 'ADDRESSID', 'ADDRESSSTATUS',
421: 'PAYMENTSTATUS', 'PENDINGREASON', 'PROTECTIONELIGIBILITY', 'EMAIL', 'SHIPPINGOPTIONNAME', 'TAXID', 'TAXIDTYPE'
422: );
423:
424: 425: 426: 427:
428: protected $_lineItemTotalExportMap = array(
429: Mage_Paypal_Model_Cart::TOTAL_SUBTOTAL => 'ITEMAMT',
430: Mage_Paypal_Model_Cart::TOTAL_TAX => 'TAXAMT',
431: Mage_Paypal_Model_Cart::TOTAL_SHIPPING => 'SHIPPINGAMT',
432: );
433: protected $_lineItemExportItemsFormat = array(
434: 'id' => 'L_NUMBER%d',
435: 'name' => 'L_NAME%d',
436: 'qty' => 'L_QTY%d',
437: 'amount' => 'L_AMT%d',
438: );
439:
440: 441: 442: 443:
444: protected $_shippingOptionsExportItemsFormat = array(
445: 'is_default' => 'L_SHIPPINGOPTIONISDEFAULT%d',
446: 'amount' => 'L_SHIPPINGOPTIONAMOUNT%d',
447: 'code' => 'L_SHIPPINGOPTIONNAME%d',
448: 'name' => 'L_SHIPPINGOPTIONLABEL%d',
449: 'tax_amount' => 'L_TAXAMT%d',
450: );
451:
452: 453: 454: 455:
456: protected $_customerBillingAgreementRequest = array('RETURNURL', 'CANCELURL', 'BILLINGTYPE');
457: protected $_customerBillingAgreementResponse = array('TOKEN');
458:
459: 460: 461: 462:
463: protected $_billingAgreementCustomerDetailsRequest = array('TOKEN');
464: protected $_billingAgreementCustomerDetailsResponse = array('EMAIL', 'PAYERID', 'PAYERSTATUS', 'SHIPTOCOUNTRYCODE',
465: 'PAYERBUSINESS'
466: );
467:
468: 469: 470: 471:
472: protected $_createBillingAgreementRequest = array('TOKEN');
473: protected $_createBillingAgreementResponse = array('BILLINGAGREEMENTID');
474:
475: 476: 477: 478:
479: protected $_updateBillingAgreementRequest = array(
480: 'REFERENCEID', 'BILLINGAGREEMENTDESCRIPTION', 'BILLINGAGREEMENTSTATUS', 'BILLINGAGREEMENTCUSTOM'
481: );
482: protected $_updateBillingAgreementResponse = array(
483: 'REFERENCEID', 'BILLINGAGREEMENTDESCRIPTION', 'BILLINGAGREEMENTSTATUS', 'BILLINGAGREEMENTCUSTOM'
484: );
485:
486: 487: 488: 489: 490:
491: protected $_doReferenceTransactionRequest = array('REFERENCEID', 'PAYMENTACTION', 'AMT', 'ITEMAMT', 'SHIPPINGAMT',
492: 'TAXAMT', 'INVNUM', 'NOTIFYURL'
493: );
494:
495: protected $_doReferenceTransactionResponse = array('BILLINGAGREEMENTID', 'TRANSACTIONID');
496:
497: 498: 499: 500: 501:
502: protected $_debugReplacePrivateDataKeys = array(
503:
504: 'ACCT', 'EXPDATE', 'CVV2', 'CARDISSUE', 'CARDSTART', 'CREDITCARDTYPE', 'USER', 'PWD', 'SIGNATURE'
505:
506: );
507:
508: 509: 510: 511:
512: protected $_supportedCcTypes = array(
513: 'VI' => 'Visa', 'MC' => 'MasterCard', 'DI' => 'Discover', 'AE' => 'Amex', 'SM' => 'Maestro', 'SO' => 'Solo');
514:
515: 516: 517: 518: 519:
520: protected $_requiredResponseParams = array(
521: self::DO_DIRECT_PAYMENT => array('ACK', 'CORRELATIONID', 'AMT')
522: );
523:
524: 525: 526: 527: 528:
529: protected $_callWarnings = array();
530:
531: 532: 533: 534: 535:
536: protected $_callErrors = array();
537:
538: 539: 540: 541: 542:
543: protected $_rawResponseNeeded = false;
544:
545: 546: 547: 548: 549:
550: public function getApiEndpoint()
551: {
552: $url = $this->getUseCertAuthentication() ? 'https://api%s.paypal.com/nvp' : 'https://api-3t%s.paypal.com/nvp';
553: return sprintf($url, $this->_config->sandboxFlag ? '.sandbox' : '');
554: }
555:
556: 557: 558: 559: 560:
561: public function getVersion()
562: {
563: return '72.0';
564: }
565:
566: 567: 568: 569: 570:
571: public function getBillingAgreementType()
572: {
573: return 'MerchantInitiatedBilling';
574: }
575:
576: 577: 578: 579: 580:
581: public function callSetExpressCheckout()
582: {
583: $this->_prepareExpressCheckoutCallRequest($this->_setExpressCheckoutRequest);
584: $request = $this->_exportToRequest($this->_setExpressCheckoutRequest);
585: $this->_exportLineItems($request);
586:
587:
588: $options = $this->getShippingOptions();
589: if ($this->getAddress()) {
590: $request = $this->_importAddresses($request);
591: $request['ADDROVERRIDE'] = 1;
592: } elseif ($options && (count($options) <= 10)) {
593: $request['CALLBACK'] = $this->getShippingOptionsCallbackUrl();
594: $request['CALLBACKTIMEOUT'] = 6;
595: $request['MAXAMT'] = $request['AMT'] + 999.00;
596: $this->_exportShippingOptions($request);
597: }
598:
599:
600: $i = 0;
601: foreach ($this->_recurringPaymentProfiles as $profile) {
602: $request["L_BILLINGTYPE{$i}"] = 'RecurringPayments';
603: $request["L_BILLINGAGREEMENTDESCRIPTION{$i}"] = $profile->getScheduleDescription();
604: $i++;
605: }
606:
607: $response = $this->call(self::SET_EXPRESS_CHECKOUT, $request);
608: $this->_importFromResponse($this->_setExpressCheckoutResponse, $response);
609: }
610:
611: 612: 613: 614:
615: function callGetExpressCheckoutDetails()
616: {
617: $this->_prepareExpressCheckoutCallRequest($this->_getExpressCheckoutDetailsRequest);
618: $request = $this->_exportToRequest($this->_getExpressCheckoutDetailsRequest);
619: $response = $this->call(self::GET_EXPRESS_CHECKOUT_DETAILS, $request);
620: $this->_importFromResponse($this->_paymentInformationResponse, $response);
621: $this->_exportAddressses($response);
622: }
623:
624: 625: 626: 627:
628: public function callDoExpressCheckoutPayment()
629: {
630: $this->_prepareExpressCheckoutCallRequest($this->_doExpressCheckoutPaymentRequest);
631: $request = $this->_exportToRequest($this->_doExpressCheckoutPaymentRequest);
632: $this->_exportLineItems($request);
633:
634: if ($this->getAddress()) {
635: $request = $this->_importAddresses($request);
636: $request['ADDROVERRIDE'] = 1;
637: }
638:
639: $response = $this->call(self::DO_EXPRESS_CHECKOUT_PAYMENT, $request);
640: $this->_importFromResponse($this->_paymentInformationResponse, $response);
641: $this->_importFromResponse($this->_doExpressCheckoutPaymentResponse, $response);
642: $this->_importFromResponse($this->_createBillingAgreementResponse, $response);
643: }
644:
645: 646: 647:
648: public function callDoDirectPayment()
649: {
650: $request = $this->_exportToRequest($this->_doDirectPaymentRequest);
651: $this->_exportLineItems($request);
652: if ($this->getAddress()) {
653: $request = $this->_importAddresses($request);
654: }
655: $response = $this->call(self::DO_DIRECT_PAYMENT, $request);
656: $this->_importFromResponse($this->_doDirectPaymentResponse, $response);
657: }
658:
659: 660: 661: 662:
663: public function callDoReferenceTransaction()
664: {
665: $request = $this->_exportToRequest($this->_doReferenceTransactionRequest);
666: $this->_exportLineItems($request);
667: $response = $this->call('DoReferenceTransaction', $request);
668: $this->_importFromResponse($this->_doReferenceTransactionResponse, $response);
669: }
670:
671: 672: 673: 674: 675:
676: public function getIsFraudDetected()
677: {
678: return in_array(11610, $this->_callWarnings);
679: }
680:
681: 682: 683:
684: public function callDoReauthorization()
685: {
686: $request = $this->_export($this->_doReauthorizationRequest);
687: $response = $this->call('DoReauthorization', $request);
688: $this->_import($response, $this->_doReauthorizationResponse);
689: }
690:
691: 692: 693: 694:
695: public function callDoCapture()
696: {
697: $this->setCompleteType($this->_getCaptureCompleteType());
698: $request = $this->_exportToRequest($this->_doCaptureRequest);
699: $response = $this->call(self::DO_CAPTURE, $request);
700: $this->_importFromResponse($this->_paymentInformationResponse, $response);
701: $this->_importFromResponse($this->_doCaptureResponse, $response);
702: }
703:
704: 705: 706: 707: 708: 709:
710: public function callDoAuthorization()
711: {
712: $request = $this->_exportToRequest($this->_doAuthorizationRequest);
713: $response = $this->call(self::DO_AUTHORIZATION, $request);
714: $this->_importFromResponse($this->_paymentInformationResponse, $response);
715: $this->_importFromResponse($this->_doAuthorizationResponse, $response);
716:
717: return $this;
718: }
719:
720:
721: 722: 723: 724:
725: public function callDoVoid()
726: {
727: $request = $this->_exportToRequest($this->_doVoidRequest);
728: $this->call(self::DO_VOID, $request);
729: }
730:
731: 732: 733: 734:
735: public function callGetTransactionDetails()
736: {
737: $request = $this->_exportToRequest($this->_getTransactionDetailsRequest);
738: $response = $this->call('GetTransactionDetails', $request);
739: $this->_importFromResponse($this->_getTransactionDetailsResponse, $response);
740: }
741:
742: 743: 744: 745:
746: public function callRefundTransaction()
747: {
748: $request = $this->_exportToRequest($this->_refundTransactionRequest);
749: if ($this->getRefundType() === Mage_Paypal_Model_Config::REFUND_TYPE_PARTIAL) {
750: $request['AMT'] = $this->getAmount();
751: }
752: $response = $this->call(self::REFUND_TRANSACTION, $request);
753: $this->_importFromResponse($this->_refundTransactionResponse, $response);
754: }
755:
756: 757: 758: 759:
760: public function callManagePendingTransactionStatus()
761: {
762: $request = $this->_exportToRequest($this->_managePendingTransactionStatusRequest);
763: if (isset($request['ACTION'])) {
764: $request['ACTION'] = $this->_filterPaymentReviewAction($request['ACTION']);
765: }
766: $response = $this->call('ManagePendingTransactionStatus', $request);
767: $this->_importFromResponse($this->_managePendingTransactionStatusResponse, $response);
768: }
769:
770: 771: 772: 773: 774:
775: public function callGetPalDetails()
776: {
777: $response = $this->call('getPalDetails', array());
778: $this->_importFromResponse($this->_getPalDetailsResponse, $response);
779: }
780:
781: 782: 783: 784: 785:
786: public function callSetCustomerBillingAgreement()
787: {
788: $request = $this->_exportToRequest($this->_customerBillingAgreementRequest);
789: $response = $this->call('SetCustomerBillingAgreement', $request);
790: $this->_importFromResponse($this->_customerBillingAgreementResponse, $response);
791: }
792:
793: 794: 795: 796: 797:
798: public function callGetBillingAgreementCustomerDetails()
799: {
800: $request = $this->_exportToRequest($this->_billingAgreementCustomerDetailsRequest);
801: $response = $this->call('GetBillingAgreementCustomerDetails', $request);
802: $this->_importFromResponse($this->_billingAgreementCustomerDetailsResponse, $response);
803: }
804:
805: 806: 807: 808:
809: public function callCreateBillingAgreement()
810: {
811: $request = $this->_exportToRequest($this->_createBillingAgreementRequest);
812: $response = $this->call('CreateBillingAgreement', $request);
813: $this->_importFromResponse($this->_createBillingAgreementResponse, $response);
814: }
815:
816: 817: 818: 819:
820: public function callUpdateBillingAgreement()
821: {
822: $request = $this->_exportToRequest($this->_updateBillingAgreementRequest);
823: try {
824: $response = $this->call('BillAgreementUpdate', $request);
825: } catch (Mage_Core_Exception $e) {
826: if (in_array(10201, $this->_callErrors)) {
827: $this->setIsBillingAgreementAlreadyCancelled(true);
828: }
829: throw $e;
830: }
831: $this->_importFromResponse($this->_updateBillingAgreementResponse, $response);
832: }
833:
834: 835: 836:
837: public function callCreateRecurringPaymentsProfile()
838: {
839: $request = $this->_exportToRequest($this->_createRecurringPaymentsProfileRequest);
840: $response = $this->call('CreateRecurringPaymentsProfile', $request);
841: $this->_importFromResponse($this->_createRecurringPaymentsProfileResponse, $response);
842: $this->_analyzeRecurringProfileStatus($this->getRecurringProfileStatus(), $this);
843: }
844:
845: 846: 847:
848: public function callManageRecurringPaymentsProfileStatus()
849: {
850: $request = $this->_exportToRequest($this->_manageRecurringPaymentsProfileStatusRequest);
851: if (isset($request['ACTION'])) {
852: $request['ACTION'] = $this->_filterRecurringProfileActionToNvp($request['ACTION']);
853: }
854: try {
855: $response = $this->call('ManageRecurringPaymentsProfileStatus', $request);
856: } catch (Mage_Core_Exception $e) {
857: if ((in_array(11556, $this->_callErrors) && 'Cancel' === $request['ACTION'])
858: || (in_array(11557, $this->_callErrors) && 'Suspend' === $request['ACTION'])
859: || (in_array(11558, $this->_callErrors) && 'Reactivate' === $request['ACTION'])
860: ) {
861: Mage::throwException(Mage::helper('paypal')->__('Unable to change status. Current status is not correspond to real status.'));
862: }
863: throw $e;
864: }
865: }
866:
867: 868: 869:
870: public function callGetRecurringPaymentsProfileDetails(Varien_Object $result)
871: {
872: $request = $this->_exportToRequest($this->_getRecurringPaymentsProfileDetailsRequest);
873: $response = $this->call('GetRecurringPaymentsProfileDetails', $request);
874: $this->_importFromResponse($this->_getRecurringPaymentsProfileDetailsResponse, $response);
875: $this->_analyzeRecurringProfileStatus($this->getStatus(), $result);
876: }
877:
878: 879: 880: 881: 882: 883:
884: public function prepareShippingOptionsCallbackAddress(array $request)
885: {
886: $address = new Varien_Object();
887: Varien_Object_Mapper::accumulateByMap($request, $address, $this->_callbackRequestMap);
888: $address->setExportedKeys(array_values($this->_callbackRequestMap));
889: $this->_applyStreetAndRegionWorkarounds($address);
890: return $address;
891: }
892:
893: 894: 895: 896: 897:
898: public function formatShippingOptionsCallback()
899: {
900: $response = array();
901: if (!$this->_exportShippingOptions($response)) {
902: $response['NO_SHIPPING_OPTION_DETAILS'] = '1';
903: }
904: $response = $this->_addMethodToRequest(self::CALLBACK_RESPONSE, $response);
905: return $this->_buildQuery($response);
906: }
907:
908: 909: 910: 911: 912: 913: 914:
915: protected function _addMethodToRequest($methodName, $request)
916: {
917: $request['METHOD'] = $methodName;
918: return $request;
919: }
920:
921: 922: 923: 924: 925: 926: 927: 928:
929: public function call($methodName, array $request)
930: {
931: $request = $this->_addMethodToRequest($methodName, $request);
932: $eachCallRequest = $this->_prepareEachCallRequest($methodName);
933: if ($this->getUseCertAuthentication()) {
934: if ($key = array_search('SIGNATURE', $eachCallRequest)) {
935: unset($eachCallRequest[$key]);
936: }
937: }
938: $request = $this->_exportToRequest($eachCallRequest, $request);
939: $debugData = array('url' => $this->getApiEndpoint(), $methodName => $request);
940:
941: try {
942: $http = new Varien_Http_Adapter_Curl();
943: $config = array(
944: 'timeout' => 30,
945: 'verifypeer' => $this->_config->verifyPeer
946: );
947:
948: if ($this->getUseProxy()) {
949: $config['proxy'] = $this->getProxyHost(). ':' . $this->getProxyPort();
950: }
951: if ($this->getUseCertAuthentication()) {
952: $config['ssl_cert'] = $this->getApiCertificate();
953: }
954: $http->setConfig($config);
955: $http->write(Zend_Http_Client::POST, $this->getApiEndpoint(), '1.1', array(), $this->_buildQuery($request));
956: $response = $http->read();
957: } catch (Exception $e) {
958: $debugData['http_error'] = array('error' => $e->getMessage(), 'code' => $e->getCode());
959: $this->_debug($debugData);
960: throw $e;
961: }
962:
963: $response = preg_split('/^\r?$/m', $response, 2);
964: $response = trim($response[1]);
965: $response = $this->_deformatNVP($response);
966:
967: $debugData['response'] = $response;
968: $this->_debug($debugData);
969:
970:
971: if ($http->getErrno()) {
972: Mage::logException(new Exception(
973: sprintf('PayPal NVP CURL connection error #%s: %s', $http->getErrno(), $http->getError())
974: ));
975: $http->close();
976:
977: Mage::throwException(Mage::helper('paypal')->__('Unable to communicate with the PayPal gateway.'));
978: }
979:
980:
981: $http->close();
982:
983: if (!$this->_validateResponse($methodName, $response)) {
984: Mage::logException(new Exception(
985: Mage::helper('paypal')->__("PayPal response hasn't required fields.")
986: ));
987: Mage::throwException(Mage::helper('paypal')->__('There was an error processing your order. Please contact us or try again later.'));
988: }
989:
990: $this->_callErrors = array();
991: if ($this->_isCallSuccessful($response)) {
992: if ($this->_rawResponseNeeded) {
993: $this->setRawSuccessResponseData($response);
994: }
995: return $response;
996: }
997: $this->_handleCallErrors($response);
998: return $response;
999: }
1000:
1001: 1002: 1003: 1004: 1005: 1006:
1007: public function setRawResponseNeeded($flag)
1008: {
1009: $this->_rawResponseNeeded = $flag;
1010: return $this;
1011: }
1012:
1013: 1014: 1015: 1016: 1017:
1018: protected function _handleCallErrors($response)
1019: {
1020: $errors = array();
1021: for ($i = 0; isset($response["L_ERRORCODE{$i}"]); $i++) {
1022: $longMessage = isset($response["L_LONGMESSAGE{$i}"])
1023: ? preg_replace('/\.$/', '', $response["L_LONGMESSAGE{$i}"]) : '';
1024: $shortMessage = preg_replace('/\.$/', '', $response["L_SHORTMESSAGE{$i}"]);
1025: $errors[] = $longMessage
1026: ? sprintf('%s (#%s: %s).', $longMessage, $response["L_ERRORCODE{$i}"], $shortMessage)
1027: : sprintf('#%s: %s.', $response["L_ERRORCODE{$i}"], $shortMessage);
1028: $this->_callErrors[] = $response["L_ERRORCODE{$i}"];
1029: }
1030: if ($errors) {
1031: $errors = implode(' ', $errors);
1032: $e = Mage::exception('Mage_Core', sprintf('PayPal NVP gateway errors: %s Correlation ID: %s. Version: %s.',
1033: $errors,
1034: isset($response['CORRELATIONID']) ? $response['CORRELATIONID'] : '',
1035: isset($response['VERSION']) ? $response['VERSION'] : ''
1036: ));
1037: Mage::logException($e);
1038: $e->setMessage(Mage::helper('paypal')->__('PayPal gateway has rejected request. %s', $errors));
1039: throw $e;
1040: }
1041: }
1042:
1043: 1044: 1045: 1046: 1047: 1048:
1049: protected function _isCallSuccessful($response)
1050: {
1051: if (!isset($response['ACK'])) {
1052: return false;
1053: }
1054:
1055: $ack = strtoupper($response['ACK']);
1056: $this->_callWarnings = array();
1057: if ($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING') {
1058:
1059: if ($ack == 'SUCCESSWITHWARNING') {
1060: for ($i = 0; isset($response["L_ERRORCODE{$i}"]); $i++) {
1061: $this->_callWarnings[] = $response["L_ERRORCODE{$i}"];
1062: }
1063: }
1064: return true;
1065: }
1066: return false;
1067: }
1068:
1069: 1070: 1071: 1072: 1073: 1074: 1075:
1076: protected function _validateResponse($method, $response)
1077: {
1078: if (isset($this->_requiredResponseParams[$method])) {
1079: foreach ($this->_requiredResponseParams[$method] as $param) {
1080: if (!isset($response[$param])) {
1081: return false;
1082: }
1083: }
1084: }
1085: return true;
1086: }
1087:
1088: 1089: 1090: 1091: 1092:
1093: protected function _deformatNVP($nvpstr)
1094: {
1095: $intial=0;
1096: $nvpArray = array();
1097:
1098: $nvpstr = strpos($nvpstr, "\r\n\r\n")!==false ? substr($nvpstr, strpos($nvpstr, "\r\n\r\n")+4) : $nvpstr;
1099:
1100: while(strlen($nvpstr)) {
1101:
1102: $keypos= strpos($nvpstr,'=');
1103:
1104: $valuepos = strpos($nvpstr,'&') ? strpos($nvpstr,'&'): strlen($nvpstr);
1105:
1106:
1107: $keyval=substr($nvpstr,$intial,$keypos);
1108: $valval=substr($nvpstr,$keypos+1,$valuepos-$keypos-1);
1109:
1110: $nvpArray[urldecode($keyval)] =urldecode( $valval);
1111: $nvpstr=substr($nvpstr,$valuepos+1,strlen($nvpstr));
1112: }
1113: return $nvpArray;
1114: }
1115:
1116: 1117: 1118: 1119: 1120: 1121: 1122:
1123: protected function _exportLineItems(array &$request, $i = 0)
1124: {
1125: if (!$this->_cart) {
1126: return;
1127: }
1128: $this->_cart->isDiscountAsItem(true);
1129: return parent::_exportLineItems($request, $i);
1130: }
1131:
1132: 1133: 1134: 1135:
1136: protected function _exportAddressses($data)
1137: {
1138: $address = new Varien_Object();
1139: Varien_Object_Mapper::accumulateByMap($data, $address, $this->_billingAddressMap);
1140: $address->setExportedKeys(array_values($this->_billingAddressMap));
1141: $this->_applyStreetAndRegionWorkarounds($address);
1142: $this->setExportedBillingAddress($address);
1143:
1144: if (isset($data['SHIPTONAME'])) {
1145: $shippingAddress = clone $address;
1146: Varien_Object_Mapper::accumulateByMap($data, $shippingAddress, $this->_shippingAddressMap);
1147: $this->_applyStreetAndRegionWorkarounds($shippingAddress);
1148:
1149: $firstName = $data['SHIPTONAME'];
1150: $lastName = null;
1151: if (isset($data['FIRSTNAME']) && $data['LASTNAME']) {
1152: $firstName = $data['FIRSTNAME'];
1153: $lastName = $data['LASTNAME'];
1154: }
1155: $shippingAddress->addData(array(
1156: 'prefix' => null,
1157: 'firstname' => $firstName,
1158: 'middlename' => null,
1159: 'lastname' => $lastName,
1160: 'suffix' => null,
1161: ));
1162: $this->setExportedShippingAddress($shippingAddress);
1163: }
1164: }
1165:
1166: 1167: 1168: 1169: 1170:
1171: protected function _applyStreetAndRegionWorkarounds(Varien_Object $address)
1172: {
1173:
1174: if ($address->hasStreet2()) {
1175: $address->setStreet(implode("\n", array($address->getStreet(), $address->getStreet2())));
1176: $address->unsStreet2();
1177: }
1178:
1179: if ($address->getCountryId() && $address->getRegion()) {
1180: $regions = Mage::getModel('directory/country')->loadByCode($address->getCountryId())->getRegionCollection()
1181: ->addRegionCodeFilter($address->getRegion())
1182: ->setPageSize(1)
1183: ;
1184: foreach ($regions as $region) {
1185: $address->setRegionId($region->getId());
1186: $address->setExportedKeys(array_merge($address->getExportedKeys(), array('region_id')));
1187: break;
1188: }
1189: }
1190: }
1191:
1192: 1193: 1194: 1195: 1196: 1197:
1198: protected function _applyCountryWorkarounds(&$request)
1199: {
1200: if (isset($request['SHIPTOCOUNTRYCODE']) && $request['SHIPTOCOUNTRYCODE'] == 'PR') {
1201: $request['SHIPTOCOUNTRYCODE'] = 'US';
1202: $request['SHIPTOSTATE'] = 'PR';
1203: }
1204: }
1205:
1206: 1207: 1208: 1209: 1210: 1211: 1212: 1213: 1214:
1215: protected function _importAddress(Varien_Object $address, array $to)
1216: {
1217: $this->setAddress($address);
1218: return $this->_importAddresses($to);
1219: }
1220:
1221: 1222: 1223: 1224: 1225: 1226:
1227: protected function _importAddresses(array $to)
1228: {
1229: $billingAddress = ($this->getBillingAddress()) ? $this->getBillingAddress() : $this->getAddress();
1230: $shippingAddress = $this->getAddress();
1231:
1232: $to = Varien_Object_Mapper::accumulateByMap(
1233: $billingAddress,
1234: $to,
1235: array_merge(array_flip($this->_billingAddressMap), $this->_billingAddressMapRequest)
1236: );
1237: if ($regionCode = $this->_lookupRegionCodeFromAddress($billingAddress)) {
1238: $to['STATE'] = $regionCode;
1239: }
1240: if (!$this->getSuppressShipping()) {
1241: $to = Varien_Object_Mapper::accumulateByMap($shippingAddress, $to, array_flip($this->_shippingAddressMap));
1242: if ($regionCode = $this->_lookupRegionCodeFromAddress($shippingAddress)) {
1243: $to['SHIPTOSTATE'] = $regionCode;
1244: }
1245: $this->_importStreetFromAddress($shippingAddress, $to, 'SHIPTOSTREET', 'SHIPTOSTREET2');
1246: $this->_importStreetFromAddress($billingAddress, $to, 'STREET', 'STREET2');
1247: $to['SHIPTONAME'] = $shippingAddress->getName();
1248: }
1249: $this->_applyCountryWorkarounds($to);
1250: return $to;
1251: }
1252:
1253: 1254: 1255: 1256: 1257: 1258:
1259: protected function _filterCcType($value)
1260: {
1261: if (isset($this->_supportedCcTypes[$value])) {
1262: return $this->_supportedCcTypes[$value];
1263: }
1264: return '';
1265: }
1266:
1267: 1268: 1269: 1270: 1271: 1272:
1273: protected function _filterToBool($value)
1274: {
1275: if ('false' === $value || '0' === $value) {
1276: return false;
1277: } elseif ('true' === $value || '1' === $value) {
1278: return true;
1279: }
1280: return $value;
1281: }
1282:
1283: 1284: 1285: 1286: 1287: 1288:
1289: protected function _filterBillFailedLater($value)
1290: {
1291: return $value ? 'AddToNextBilling' : 'NoAutoBill';
1292: }
1293:
1294: 1295: 1296: 1297: 1298: 1299:
1300: protected function _filterPeriodUnit($value)
1301: {
1302: switch ($value) {
1303: case 'day': return 'Day';
1304: case 'week': return 'Week';
1305: case 'semi_month': return 'SemiMonth';
1306: case 'month': return 'Month';
1307: case 'year': return 'Year';
1308: }
1309: }
1310:
1311: 1312: 1313: 1314: 1315: 1316:
1317: protected function _filterInitialAmountMayFail($value)
1318: {
1319: return $value ? 'ContinueOnFailure' : 'CancelOnFailure';
1320: }
1321:
1322: 1323: 1324: 1325: 1326: 1327:
1328: protected function _filterBillingAgreementStatus($value)
1329: {
1330: switch ($value) {
1331: case 'canceled': return 'Canceled';
1332: case 'active': return 'Active';
1333: }
1334: }
1335:
1336: 1337: 1338: 1339: 1340: 1341:
1342: protected function _filterPaymentStatusFromNvpToInfo($value)
1343: {
1344: switch ($value) {
1345: case 'None': return Mage_Paypal_Model_Info::PAYMENTSTATUS_NONE;
1346: case 'Completed': return Mage_Paypal_Model_Info::PAYMENTSTATUS_COMPLETED;
1347: case 'Denied': return Mage_Paypal_Model_Info::PAYMENTSTATUS_DENIED;
1348: case 'Expired': return Mage_Paypal_Model_Info::PAYMENTSTATUS_EXPIRED;
1349: case 'Failed': return Mage_Paypal_Model_Info::PAYMENTSTATUS_FAILED;
1350: case 'In-Progress': return Mage_Paypal_Model_Info::PAYMENTSTATUS_INPROGRESS;
1351: case 'Pending': return Mage_Paypal_Model_Info::PAYMENTSTATUS_PENDING;
1352: case 'Refunded': return Mage_Paypal_Model_Info::PAYMENTSTATUS_REFUNDED;
1353: case 'Partially-Refunded': return Mage_Paypal_Model_Info::PAYMENTSTATUS_REFUNDEDPART;
1354: case 'Reversed': return Mage_Paypal_Model_Info::PAYMENTSTATUS_REVERSED;
1355: case 'Canceled-Reversal': return Mage_Paypal_Model_Info::PAYMENTSTATUS_UNREVERSED;
1356: case 'Processed': return Mage_Paypal_Model_Info::PAYMENTSTATUS_PROCESSED;
1357: case 'Voided': return Mage_Paypal_Model_Info::PAYMENTSTATUS_VOIDED;
1358: }
1359: }
1360:
1361: 1362: 1363: 1364: 1365: 1366:
1367: protected function _filterPaymentReviewAction($value)
1368: {
1369: switch ($value) {
1370: case Mage_Paypal_Model_Pro::PAYMENT_REVIEW_ACCEPT:
1371: return 'Accept';
1372: case Mage_Paypal_Model_Pro::PAYMENT_REVIEW_DENY:
1373: return 'Deny';
1374: }
1375: }
1376:
1377: 1378: 1379: 1380: 1381: 1382:
1383: protected function _filterRecurringProfileActionToNvp($value)
1384: {
1385: switch ($value) {
1386: case 'cancel': return 'Cancel';
1387: case 'suspend': return 'Suspend';
1388: case 'activate': return 'Reactivate';
1389: }
1390: }
1391:
1392: 1393: 1394: 1395: 1396: 1397:
1398: protected function _analyzeRecurringProfileStatus($value, Varien_Object $result)
1399: {
1400: switch ($value) {
1401: case 'ActiveProfile':
1402: case 'Active':
1403: $result->setIsProfileActive(true);
1404: break;
1405: case 'PendingProfile':
1406: $result->setIsProfilePending(true);
1407: break;
1408: case 'CancelledProfile':
1409: case 'Cancelled':
1410: $result->setIsProfileCanceled(true);
1411: break;
1412: case 'SuspendedProfile':
1413: case 'Suspended':
1414: $result->setIsProfileSuspended(true);
1415: break;
1416: case 'ExpiredProfile':
1417: case 'Expired':
1418: $result->setIsProfileExpired(true);
1419: break;
1420: }
1421: }
1422:
1423: 1424: 1425: 1426: 1427: 1428:
1429: protected function _getCaptureCompleteType()
1430: {
1431: return ($this->getIsCaptureComplete())
1432: ? $this->_captureTypeComplete
1433: : $this->_captureTypeNotcomplete;
1434: }
1435:
1436: 1437: 1438: 1439: 1440: 1441:
1442: protected function _prepareEachCallRequest($methodName)
1443: {
1444: $expressCheckooutMetods = array(
1445: self::SET_EXPRESS_CHECKOUT, self::GET_EXPRESS_CHECKOUT_DETAILS, self::DO_EXPRESS_CHECKOUT_PAYMENT
1446: );
1447: if (!in_array($methodName, $expressCheckooutMetods) || !$this->_config->shouldUseUnilateralPayments()) {
1448: return $this->_eachCallRequest;
1449: }
1450: return array_diff($this->_eachCallRequest, array('USER', 'PWD', 'SIGNATURE'));
1451: }
1452:
1453: 1454: 1455: 1456: 1457:
1458: protected function _prepareExpressCheckoutCallRequest(&$requestFields)
1459: {
1460: if (!$this->_config->shouldUseUnilateralPayments()) {
1461: if ($key = array_search('SUBJECT', $requestFields)) {
1462: unset($requestFields[$key]);
1463: }
1464: }
1465: }
1466: }
1467: