Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Nvp.php
Go to the documentation of this file.
1 <?php
8 
11 
22 {
26  const DO_DIRECT_PAYMENT = 'DoDirectPayment';
27 
28  const DO_CAPTURE = 'DoCapture';
29 
30  const DO_AUTHORIZATION = 'DoAuthorization';
31 
32  const DO_VOID = 'DoVoid';
33 
34  const REFUND_TRANSACTION = 'RefundTransaction';
35 
36  const SET_EXPRESS_CHECKOUT = 'SetExpressCheckout';
37 
38  const GET_EXPRESS_CHECKOUT_DETAILS = 'GetExpressCheckoutDetails';
39 
40  const DO_EXPRESS_CHECKOUT_PAYMENT = 'DoExpressCheckoutPayment';
41 
42  const CALLBACK_RESPONSE = 'CallbackResponse';
43 
47  const PENDING_TRANSACTION_ACCEPT = 'Accept';
48 
49  const PENDING_TRANSACTION_DENY = 'Deny';
50 
56  protected $_captureTypeComplete = 'Complete';
57 
63  protected $_captureTypeNotcomplete = 'NotComplete';
64 
70  protected $_globalMap = [
71  // each call
72  'VERSION' => 'version',
73  'USER' => 'api_username',
74  'PWD' => 'api_password',
75  'SIGNATURE' => 'api_signature',
76  'BUTTONSOURCE' => 'build_notation_code',
77 
78  // for Unilateral payments
79  'SUBJECT' => 'business_account',
80 
81  // commands
82  'PAYMENTACTION' => 'payment_action',
83  'RETURNURL' => 'return_url',
84  'CANCELURL' => 'cancel_url',
85  'INVNUM' => 'inv_num',
86  'TOKEN' => 'token',
87  'CORRELATIONID' => 'correlation_id',
88  'SOLUTIONTYPE' => 'solution_type',
89  'GIROPAYCANCELURL' => 'giropay_cancel_url',
90  'GIROPAYSUCCESSURL' => 'giropay_success_url',
91  'BANKTXNPENDINGURL' => 'giropay_bank_txn_pending_url',
92  'IPADDRESS' => 'ip_address',
93  'NOTIFYURL' => 'notify_url',
94  'RETURNFMFDETAILS' => 'fraud_management_filters_enabled',
95  'NOTE' => 'note',
96  'REFUNDTYPE' => 'refund_type',
97  'ACTION' => 'action',
98  'REDIRECTREQUIRED' => 'redirect_required',
99  'SUCCESSPAGEREDIRECTREQUESTED' => 'redirect_requested',
100  'REQBILLINGADDRESS' => 'require_billing_address',
101  // style settings
102  'PAGESTYLE' => 'page_style',
103  'HDRIMG' => 'hdrimg',
104  'HDRBORDERCOLOR' => 'hdrbordercolor',
105  'HDRBACKCOLOR' => 'hdrbackcolor',
106  'PAYFLOWCOLOR' => 'payflowcolor',
107  'LOCALECODE' => 'locale_code',
108  'PAL' => 'pal',
109  'USERSELECTEDFUNDINGSOURCE' => 'funding_source',
110 
111  // transaction info
112  'TRANSACTIONID' => 'transaction_id',
113  'AUTHORIZATIONID' => 'authorization_id',
114  'REFUNDTRANSACTIONID' => 'refund_transaction_id',
115  'COMPLETETYPE' => 'complete_type',
116  'AMT' => 'amount',
117  'ITEMAMT' => 'subtotal_amount',
118  'GROSSREFUNDAMT' => 'refunded_amount', // possible mistake, check with API reference
119 
120  // payment/billing info
121  'CURRENCYCODE' => 'currency_code',
122  'PAYMENTSTATUS' => 'payment_status',
123  'PENDINGREASON' => 'pending_reason',
124  'PROTECTIONELIGIBILITY' => 'protection_eligibility',
125  'PAYERID' => 'payer_id',
126  'PAYERSTATUS' => 'payer_status',
127  'ADDRESSID' => 'address_id',
128  'ADDRESSSTATUS' => 'address_status',
129  'EMAIL' => 'email',
130 
131  // backwards compatibility
132  'FIRSTNAME' => 'firstname',
133  'LASTNAME' => 'lastname',
134 
135  // shipping rate
136  'SHIPPINGOPTIONNAME' => 'shipping_rate_code',
137  'NOSHIPPING' => 'suppress_shipping',
138 
139  // paypal direct credit card information
140  'CREDITCARDTYPE' => 'credit_card_type',
141  'ACCT' => 'credit_card_number',
142  'EXPDATE' => 'credit_card_expiration_date',
143  'CVV2' => 'credit_card_cvv2',
144  'STARTDATE' => 'maestro_solo_issue_date',
145  'ISSUENUMBER' => 'maestro_solo_issue_number',
146  'CVV2MATCH' => 'cvv2_check_result',
147  'AVSCODE' => 'avs_result',
148 
149  'SHIPPINGAMT' => 'shipping_amount',
150  'TAXAMT' => 'tax_amount',
151  'INITAMT' => 'init_amount',
152  'STATUS' => 'status',
153 
154  //Next two fields are used for Brazil only
155  'TAXID' => 'buyer_tax_id',
156  'TAXIDTYPE' => 'buyer_tax_id_type',
157 
158  'BILLINGAGREEMENTID' => 'billing_agreement_id',
159  'REFERENCEID' => 'reference_id',
160  'BILLINGAGREEMENTSTATUS' => 'billing_agreement_status',
161  'BILLINGTYPE' => 'billing_type',
162  'SREET' => 'street',
163  'CITY' => 'city',
164  'STATE' => 'state',
165  'COUNTRYCODE' => 'countrycode',
166  'ZIP' => 'zip',
167  'PAYERBUSINESS' => 'payer_business',
168  ];
169 
176  'AMT' => 'formatPrice',
177  'ITEMAMT' => 'formatPrice',
178  'TRIALAMT' => 'formatPrice',
179  'SHIPPINGAMT' => 'formatPrice',
180  'TAXAMT' => 'formatPrice',
181  'INITAMT' => 'formatPrice',
182  'CREDITCARDTYPE' => '_filterCcType',
183  'AUTOBILLAMT' => '_filterBillFailedLater',
184  'BILLINGPERIOD' => '_filterPeriodUnit',
185  'TRIALBILLINGPERIOD' => '_filterPeriodUnit',
186  'FAILEDINITAMTACTION' => '_filterInitialAmountMayFail',
187  'BILLINGAGREEMENTSTATUS' => '_filterBillingAgreementStatus',
188  'NOSHIPPING' => '_filterInt',
189  ];
190 
197  'REDIRECTREQUIRED' => '_filterToBool',
198  'SUCCESSPAGEREDIRECTREQUESTED' => '_filterToBool',
199  'PAYMENTSTATUS' => '_filterPaymentStatusFromNvpToInfo',
200  ];
201 
207  protected $_eachCallRequest = ['VERSION', 'USER', 'PWD', 'SIGNATURE', 'BUTTONSOURCE'];
208 
215  'PAYMENTACTION',
216  'AMT',
217  'CURRENCYCODE',
218  'RETURNURL',
219  'CANCELURL',
220  'INVNUM',
221  'SOLUTIONTYPE',
222  'NOSHIPPING',
223  'GIROPAYCANCELURL',
224  'GIROPAYSUCCESSURL',
225  'BANKTXNPENDINGURL',
226  'PAGESTYLE',
227  'HDRIMG',
228  'HDRBORDERCOLOR',
229  'HDRBACKCOLOR',
230  'PAYFLOWCOLOR',
231  'LOCALECODE',
232  'BILLINGTYPE',
233  'SUBJECT',
234  'ITEMAMT',
235  'SHIPPINGAMT',
236  'TAXAMT',
237  'REQBILLINGADDRESS',
238  'USERSELECTEDFUNDINGSOURCE',
239  ];
240 
246  protected $_setExpressCheckoutResponse = ['TOKEN'];
247 
253  protected $_getExpressCheckoutDetailsRequest = ['TOKEN', 'SUBJECT'];
254 
261  'TOKEN',
262  'PAYERID',
263  'PAYMENTACTION',
264  'AMT',
265  'CURRENCYCODE',
266  'IPADDRESS',
267  'BUTTONSOURCE',
268  'NOTIFYURL',
269  'RETURNFMFDETAILS',
270  'SUBJECT',
271  'ITEMAMT',
272  'SHIPPINGAMT',
273  'TAXAMT',
274  ];
275 
282  'TRANSACTIONID',
283  'AMT',
284  'PAYMENTSTATUS',
285  'PENDINGREASON',
286  'REDIRECTREQUIRED',
287  ];
288 
295  'PAYMENTACTION',
296  'IPADDRESS',
297  'RETURNFMFDETAILS',
298  'AMT',
299  'CURRENCYCODE',
300  'INVNUM',
301  'NOTIFYURL',
302  'EMAIL',
303  'ITEMAMT',
304  'SHIPPINGAMT',
305  'TAXAMT',
306  'CREDITCARDTYPE',
307  'ACCT',
308  'EXPDATE',
309  'CVV2',
310  'STARTDATE',
311  'ISSUENUMBER',
312  'AUTHSTATUS3DS',
313  'MPIVENDOR3DS',
314  'CAVV',
315  'ECI3DS',
316  'XID',
317  ];
318 
325  'TRANSACTIONID',
326  'AMT',
327  'AVSCODE',
328  'CVV2MATCH',
329  'VPAS',
330  'ECISUBMITTED3DS',
331  ];
332 
338  protected $_doReauthorizationRequest = ['AUTHORIZATIONID', 'AMT', 'CURRENCYCODE'];
339 
346  'AUTHORIZATIONID',
347  'PAYMENTSTATUS',
348  'PENDINGREASON',
349  'PROTECTIONELIGIBILITY',
350  ];
351 
357  protected $_doCaptureRequest = ['AUTHORIZATIONID', 'COMPLETETYPE', 'AMT', 'CURRENCYCODE', 'NOTE', 'INVNUM'];
358 
364  protected $_doCaptureResponse = ['TRANSACTIONID', 'CURRENCYCODE', 'AMT', 'PAYMENTSTATUS', 'PENDINGREASON'];
365 
371  protected $_doAuthorizationRequest = ['TRANSACTIONID', 'AMT', 'CURRENCYCODE'];
372 
378  protected $_doAuthorizationResponse = ['TRANSACTIONID', 'AMT'];
379 
385  protected $_doVoidRequest = ['AUTHORIZATIONID', 'NOTE'];
386 
392  protected $_getTransactionDetailsRequest = ['TRANSACTIONID'];
393 
400  'PAYERID',
401  'FIRSTNAME',
402  'LASTNAME',
403  'TRANSACTIONID',
404  'PARENTTRANSACTIONID',
405  'CURRENCYCODE',
406  'AMT',
407  'PAYMENTSTATUS',
408  'PENDINGREASON',
409  ];
410 
416  protected $_refundTransactionRequest = ['TRANSACTIONID', 'REFUNDTYPE', 'CURRENCYCODE', 'NOTE'];
417 
423  protected $_refundTransactionResponse = ['REFUNDTRANSACTIONID', 'GROSSREFUNDAMT'];
424 
430  protected $_managePendingTransactionStatusRequest = ['TRANSACTIONID', 'ACTION'];
431 
437  protected $_managePendingTransactionStatusResponse = ['TRANSACTIONID', 'STATUS'];
438 
444  protected $_getPalDetailsResponse = ['PAL'];
445 
451  protected $_billingAddressMap = [
452  'BUSINESS' => 'company',
453  'NOTETEXT' => 'customer_notes',
454  'EMAIL' => 'email',
455  'FIRSTNAME' => 'firstname',
456  'LASTNAME' => 'lastname',
457  'MIDDLENAME' => 'middlename',
458  'SALUTATION' => 'prefix',
459  'SUFFIX' => 'suffix',
460  'COUNTRYCODE' => 'country_id', // iso-3166 two-character code
461  'STATE' => 'region',
462  'CITY' => 'city',
463  'STREET' => 'street',
464  'STREET2' => 'street2',
465  'ZIP' => 'postcode',
466  'PHONENUM' => 'telephone',
467  ];
468 
476 
481  protected $_shippingAddressMap = [
482  'SHIPTOCOUNTRYCODE' => 'country_id',
483  'SHIPTOSTATE' => 'region',
484  'SHIPTOCITY' => 'city',
485  'SHIPTOSTREET' => 'street',
486  'SHIPTOSTREET2' => 'street2',
487  'SHIPTOZIP' => 'postcode',
488  'SHIPTOPHONENUM' => 'telephone',
489  // 'SHIPTONAME' will be treated manually in address import/export methods
490  ];
491 
496  protected $_callbackRequestMap = [
497  'SHIPTOCOUNTRY' => 'country_id',
498  'SHIPTOSTATE' => 'region',
499  'SHIPTOCITY' => 'city',
500  'SHIPTOSTREET' => 'street',
501  'SHIPTOSTREET2' => 'street2',
502  'SHIPTOZIP' => 'postcode',
503  ];
504 
510  'PAYERID',
511  'PAYERSTATUS',
512  'CORRELATIONID',
513  'ADDRESSID',
514  'ADDRESSSTATUS',
515  'PAYMENTSTATUS',
516  'PENDINGREASON',
517  'PROTECTIONELIGIBILITY',
518  'EMAIL',
519  'SHIPPINGOPTIONNAME',
520  'TAXID',
521  'TAXIDTYPE',
522  ];
523 
529  Cart::AMOUNT_SUBTOTAL => 'ITEMAMT',
530  Cart::AMOUNT_TAX => 'TAXAMT',
531  Cart::AMOUNT_SHIPPING => 'SHIPPINGAMT',
532  ];
533 
539  'id' => 'L_NUMBER%d',
540  'name' => 'L_NAME%d',
541  'qty' => 'L_QTY%d',
542  'amount' => 'L_AMT%d',
543  ];
544 
550  'is_default' => 'L_SHIPPINGOPTIONISDEFAULT%d',
551  'amount' => 'L_SHIPPINGOPTIONAMOUNT%d',
552  'code' => 'L_SHIPPINGOPTIONNAME%d',
553  'name' => 'L_SHIPPINGOPTIONLABEL%d',
554  'tax_amount' => 'L_TAXAMT%d',
555  ];
556 
562  protected $_customerBillingAgreementRequest = ['RETURNURL', 'CANCELURL', 'BILLINGTYPE'];
563 
569  protected $_customerBillingAgreementResponse = ['TOKEN'];
570 
577 
584  'EMAIL',
585  'PAYERID',
586  'PAYERSTATUS',
587  'SHIPTOCOUNTRYCODE',
588  'PAYERBUSINESS',
589  ];
590 
596  protected $_createBillingAgreementRequest = ['TOKEN'];
597 
603  protected $_createBillingAgreementResponse = ['BILLINGAGREEMENTID'];
604 
611  'REFERENCEID',
612  'BILLINGAGREEMENTDESCRIPTION',
613  'BILLINGAGREEMENTSTATUS',
614  'BILLINGAGREEMENTCUSTOM',
615  ];
616 
623  'REFERENCEID',
624  'BILLINGAGREEMENTDESCRIPTION',
625  'BILLINGAGREEMENTSTATUS',
626  'BILLINGAGREEMENTCUSTOM',
627  ];
628 
635  'REFERENCEID',
636  'PAYMENTACTION',
637  'AMT',
638  'ITEMAMT',
639  'SHIPPINGAMT',
640  'TAXAMT',
641  'INVNUM',
642  'NOTIFYURL',
643  'CURRENCYCODE',
644  ];
645 
651  protected $_doReferenceTransactionResponse = ['BILLINGAGREEMENTID', 'TRANSACTIONID'];
652 
659  'ACCT',
660  'EXPDATE',
661  'CVV2',
662  'CARDISSUE',
663  'CARDSTART',
664  'CREDITCARDTYPE',
665  'USER',
666  'PWD',
667  'SIGNATURE',
668  ];
669 
675  protected $_supportedCcTypes = [
676  'VI' => 'Visa',
677  'MC' => 'MasterCard',
678  'DI' => 'Discover',
679  'AE' => 'Amex',
680  'SM' => 'Maestro',
681  'SO' => 'Solo',
682  ];
683 
689  protected $_requiredResponseParams = [self::DO_DIRECT_PAYMENT => ['ACK', 'CORRELATIONID', 'AMT']];
690 
696  protected $_callWarnings = [];
697 
703  protected $_callErrors = [];
704 
710  protected $_rawResponseNeeded = false;
711 
715  protected $_countryFactory;
716 
721 
726 
730  protected $_curlFactory;
731 
737  protected $_headers = [];
738 
752  public function __construct(
753  \Magento\Customer\Helper\Address $customerAddress,
754  \Psr\Log\LoggerInterface $logger,
756  \Magento\Framework\Locale\ResolverInterface $localeResolver,
757  \Magento\Directory\Model\RegionFactory $regionFactory,
758  \Magento\Directory\Model\CountryFactory $countryFactory,
759  \Magento\Paypal\Model\Api\ProcessableExceptionFactory $processableExceptionFactory,
760  \Magento\Framework\Exception\LocalizedExceptionFactory $frameworkExceptionFactory,
761  \Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory,
762  array $data = []
763  ) {
764  parent::__construct($customerAddress, $logger, $customLogger, $localeResolver, $regionFactory, $data);
765  $this->_countryFactory = $countryFactory;
766  $this->_processableExceptionFactory = $processableExceptionFactory;
767  $this->_frameworkExceptionFactory = $frameworkExceptionFactory;
768  $this->_curlFactory = $curlFactory;
769  }
770 
776  public function getApiEndpoint()
777  {
778  $url = $this->getUseCertAuthentication() ? 'https://api%s.paypal.com/nvp' : 'https://api-3t%s.paypal.com/nvp';
779  return sprintf($url, $this->_config->getValue('sandboxFlag') ? '.sandbox' : '');
780  }
781 
787  public function getVersion()
788  {
789  return '72.0';
790  }
791 
797  public function getBillingAgreementType()
798  {
799  return 'MerchantInitiatedBilling';
800  }
801 
810  public function callSetExpressCheckout()
811  {
812  $this->_prepareExpressCheckoutCallRequest($this->_setExpressCheckoutRequest);
813  $request = $this->_exportToRequest($this->_setExpressCheckoutRequest);
814  $this->_exportLineItems($request);
815 
816  // import/suppress shipping address, if any
817  $options = $this->getShippingOptions();
818  if ($this->getAddress()) {
820  $request['ADDROVERRIDE'] = 1;
821  } elseif ($options && count($options) <= 10) {
822  // doesn't support more than 10 shipping options
823  $request['CALLBACK'] = $this->getShippingOptionsCallbackUrl();
824  $request['CALLBACKTIMEOUT'] = 6;
825  // max value
826  $request['MAXAMT'] = $request['AMT'] + 999.00;
827  // it is impossible to calculate max amount
829  }
830 
831  $response = $this->call(self::SET_EXPRESS_CHECKOUT, $request);
832  $this->_importFromResponse($this->_setExpressCheckoutResponse, $response);
833  }
834 
842  {
843  $this->_prepareExpressCheckoutCallRequest($this->_getExpressCheckoutDetailsRequest);
844  $request = $this->_exportToRequest($this->_getExpressCheckoutDetailsRequest);
845  $response = $this->call(self::GET_EXPRESS_CHECKOUT_DETAILS, $request);
846  $this->_importFromResponse($this->_paymentInformationResponse, $response);
847  $this->_exportAddresses($response);
848  }
849 
857  {
858  $this->_prepareExpressCheckoutCallRequest($this->_doExpressCheckoutPaymentRequest);
859  $request = $this->_exportToRequest($this->_doExpressCheckoutPaymentRequest);
860  $this->_exportLineItems($request);
861 
862  if ($this->getAddress()) {
864  $request['ADDROVERRIDE'] = 1;
865  }
866 
867  $response = $this->call(self::DO_EXPRESS_CHECKOUT_PAYMENT, $request);
868  $this->_importFromResponse($this->_paymentInformationResponse, $response);
869  $this->_importFromResponse($this->_doExpressCheckoutPaymentResponse, $response);
870  $this->_importFromResponse($this->_createBillingAgreementResponse, $response);
871  }
872 
878  public function callDoDirectPayment()
879  {
880  $request = $this->_exportToRequest($this->_doDirectPaymentRequest);
881  $this->_exportLineItems($request);
882  if ($this->getAddress()) {
884  }
885  $response = $this->call(self::DO_DIRECT_PAYMENT, $request);
886  $this->_importFromResponse($this->_doDirectPaymentResponse, $response);
887  }
888 
895  public function callDoReferenceTransaction()
896  {
897  $request = $this->_exportToRequest($this->_doReferenceTransactionRequest);
898  $this->_exportLineItems($request);
899  $response = $this->call('DoReferenceTransaction', $request);
900  $this->_importFromResponse($this->_doReferenceTransactionResponse, $response);
901  }
902 
909  public function getIsFraudDetected()
910  {
911  return in_array(11610, $this->_callWarnings);
912  }
913 
919  public function callDoReauthorization()
920  {
921  $request = $this->_exportToRequest($this->_doReauthorizationRequest);
922  $response = $this->call('DoReauthorization', $request);
923  $this->_importFromResponse($this->_doReauthorizationResponse, $response);
924  }
925 
932  public function callDoCapture()
933  {
934  $this->setCompleteType($this->_getCaptureCompleteType());
935  $request = $this->_exportToRequest($this->_doCaptureRequest);
936  $response = $this->call(self::DO_CAPTURE, $request);
937  $this->_importFromResponse($this->_paymentInformationResponse, $response);
938  $this->_importFromResponse($this->_doCaptureResponse, $response);
939  }
940 
947  public function callDoAuthorization()
948  {
949  $request = $this->_exportToRequest($this->_doAuthorizationRequest);
950  $response = $this->call(self::DO_AUTHORIZATION, $request);
951  $this->_importFromResponse($this->_paymentInformationResponse, $response);
952  $this->_importFromResponse($this->_doAuthorizationResponse, $response);
953 
954  return $this;
955  }
956 
963  public function callDoVoid()
964  {
965  $request = $this->_exportToRequest($this->_doVoidRequest);
966  $this->call(self::DO_VOID, $request);
967  }
968 
975  public function callGetTransactionDetails()
976  {
977  $request = $this->_exportToRequest($this->_getTransactionDetailsRequest);
978  $response = $this->call('GetTransactionDetails', $request);
979  $this->_importFromResponse($this->_getTransactionDetailsResponse, $response);
980  }
981 
988  public function callRefundTransaction()
989  {
990  $request = $this->_exportToRequest($this->_refundTransactionRequest);
991  if ($this->getRefundType() === \Magento\Paypal\Model\Config::REFUND_TYPE_PARTIAL) {
992  $request['AMT'] = $this->formatPrice($this->getAmount());
993  }
994  $response = $this->call(self::REFUND_TRANSACTION, $request);
995  $this->_importFromResponse($this->_refundTransactionResponse, $response);
996  }
997 
1005  {
1006  $request = $this->_exportToRequest($this->_managePendingTransactionStatusRequest);
1007  if (isset($request['ACTION'])) {
1008  $request['ACTION'] = $this->_filterPaymentReviewAction($request['ACTION']);
1009  }
1010  $response = $this->call('ManagePendingTransactionStatus', $request);
1011  $this->_importFromResponse($this->_managePendingTransactionStatusResponse, $response);
1012  }
1013 
1021  public function callGetPalDetails()
1022  {
1023  $response = $this->call('getPalDetails', []);
1024  $this->_importFromResponse($this->_getPalDetailsResponse, $response);
1025  }
1026 
1034  {
1035  $request = $this->_exportToRequest($this->_customerBillingAgreementRequest);
1036  $response = $this->call('SetCustomerBillingAgreement', $request);
1037  $this->_importFromResponse($this->_customerBillingAgreementResponse, $response);
1038  }
1039 
1047  {
1048  $request = $this->_exportToRequest($this->_billingAgreementCustomerDetailsRequest);
1049  $response = $this->call('GetBillingAgreementCustomerDetails', $request);
1050  $this->_importFromResponse($this->_billingAgreementCustomerDetailsResponse, $response);
1051  }
1052 
1058  public function callCreateBillingAgreement()
1059  {
1060  $request = $this->_exportToRequest($this->_createBillingAgreementRequest);
1061  $response = $this->call('CreateBillingAgreement', $request);
1062  $this->_importFromResponse($this->_createBillingAgreementResponse, $response);
1063  }
1064 
1070  public function callUpdateBillingAgreement()
1071  {
1072  $request = $this->_exportToRequest($this->_updateBillingAgreementRequest);
1073  try {
1074  $response = $this->call('BillAgreementUpdate', $request);
1075  } catch (\Magento\Framework\Exception\LocalizedException $e) {
1076  if (in_array(10201, $this->_callErrors)) {
1077  $this->setIsBillingAgreementAlreadyCancelled(true);
1078  }
1079  throw $e;
1080  }
1081  $this->_importFromResponse($this->_updateBillingAgreementResponse, $response);
1082  }
1083 
1091  {
1092  $address = new \Magento\Framework\DataObject();
1093  \Magento\Framework\DataObject\Mapper::accumulateByMap($request, $address, $this->_callbackRequestMap);
1094  $address->setExportedKeys(array_values($this->_callbackRequestMap));
1096  return $address;
1097  }
1098 
1105  {
1106  $response = [];
1107  if (!$this->_exportShippingOptions($response)) {
1108  $response['NO_SHIPPING_OPTION_DETAILS'] = '1';
1109  }
1110  $response = $this->_addMethodToRequest(self::CALLBACK_RESPONSE, $response);
1111  return $this->_buildQuery($response);
1112  }
1113 
1121  protected function _addMethodToRequest($methodName, $request)
1122  {
1123  $request['METHOD'] = $methodName;
1124  return $request;
1125  }
1126 
1134  protected function _postProcessResponse($response)
1135  {
1136  foreach ($response as $key => $value) {
1137  $pos = strpos($key, '[');
1138 
1139  if ($pos === false) {
1140  continue;
1141  }
1142 
1143  unset($response[$key]);
1144 
1145  if ($pos !== 0) {
1146  $modifiedKey = substr($key, 0, $pos);
1147  $response[$modifiedKey] = $value;
1148  }
1149  }
1150 
1151  return $response;
1152  }
1153 
1163  public function call($methodName, array $request)
1164  {
1165  $request = $this->_addMethodToRequest($methodName, $request);
1166  $eachCallRequest = $this->_prepareEachCallRequest($methodName);
1167  if ($this->getUseCertAuthentication()) {
1168  $key = array_search('SIGNATURE', $eachCallRequest);
1169  if ($key) {
1170  unset($eachCallRequest[$key]);
1171  }
1172  }
1173  $request = $this->_exportToRequest($eachCallRequest, $request);
1174  $debugData = ['url' => $this->getApiEndpoint(), $methodName => $request];
1175 
1176  try {
1177  $http = $this->_curlFactory->create();
1178  $config = ['timeout' => 60, 'verifypeer' => $this->_config->getValue('verifyPeer')];
1179  if ($this->getUseProxy()) {
1180  $config['proxy'] = $this->getProxyHost() . ':' . $this->getProxyPort();
1181  }
1182  if ($this->getUseCertAuthentication()) {
1183  $config['ssl_cert'] = $this->getApiCertificate();
1184  }
1185  $http->setConfig($config);
1186  $http->write(
1188  $this->getApiEndpoint(),
1189  '1.1',
1190  $this->_headers,
1191  $this->_buildQuery($request)
1192  );
1193  $response = $http->read();
1194  } catch (\Exception $e) {
1195  $debugData['http_error'] = ['error' => $e->getMessage(), 'code' => $e->getCode()];
1196  $this->_debug($debugData);
1197  throw $e;
1198  }
1199 
1200  $response = preg_split('/^\r?$/m', $response, 2);
1201  $response = trim($response[1]);
1202  $response = $this->_deformatNVP($response);
1203 
1204  $debugData['response'] = $response;
1205  $this->_debug($debugData);
1206 
1208 
1209  // handle transport error
1210  if ($http->getErrno()) {
1211  $this->_logger->critical(
1212  new \Exception(
1213  sprintf('PayPal NVP CURL connection error #%s: %s', $http->getErrno(), $http->getError())
1214  )
1215  );
1216  $http->close();
1217 
1218  throw new \Magento\Framework\Exception\LocalizedException(
1219  __('Payment Gateway is unreachable at the moment. Please use another payment option.')
1220  );
1221  }
1222 
1223  // cUrl resource must be closed after checking it for errors
1224  $http->close();
1225 
1226  if (!$this->_validateResponse($methodName, $response)) {
1227  $this->_logger->critical(new \Exception(__('PayPal response hasn\'t required fields.')));
1228  throw new \Magento\Framework\Exception\LocalizedException(
1229  __('Something went wrong while processing your order.')
1230  );
1231  }
1232 
1233  $this->_callErrors = [];
1234  if ($this->_isCallSuccessful($response)) {
1235  if ($this->_rawResponseNeeded) {
1236  $this->setRawSuccessResponseData($response);
1237  }
1238  return $response;
1239  }
1240  $this->_handleCallErrors($response);
1241  return $response;
1242  }
1243 
1250  public function setRawResponseNeeded($flag)
1251  {
1252  $this->_rawResponseNeeded = $flag;
1253  return $this;
1254  }
1255 
1264  protected function _handleCallErrors($response)
1265  {
1267  if (empty($errors)) {
1268  return;
1269  }
1270 
1271  $errorMessages = [];
1272  foreach ($errors as $error) {
1273  $errorMessages[] = $error['message'];
1274  $this->_callErrors[] = $error['code'];
1275  }
1276  $errorMessages = implode(' ', $errorMessages);
1277 
1278  $exceptionLogMessage = sprintf(
1279  'PayPal NVP gateway errors: %s Correlation ID: %s. Version: %s.',
1280  $errorMessages,
1281  isset($response['CORRELATIONID']) ? $response['CORRELATIONID'] : '',
1282  isset($response['VERSION']) ? $response['VERSION'] : ''
1283  );
1284  $this->_logger->critical($exceptionLogMessage);
1285 
1291  if (in_array((string)ProcessableException::API_TRANSACTION_HAS_BEEN_COMPLETED, $this->_callErrors)) {
1292  return;
1293  }
1294 
1295  $exceptionPhrase = __('PayPal gateway has rejected request. %1', $errorMessages);
1296 
1298  $firstError = $errors[0]['code'];
1299  $exception = $this->_isProcessableError($firstError)
1300  ? $this->_processableExceptionFactory->create(
1301  ['phrase' => $exceptionPhrase, 'code' => $firstError]
1302  )
1303  : $this->_frameworkExceptionFactory->create(
1304  ['phrase' => $exceptionPhrase]
1305  );
1306 
1307  throw $exception;
1308  }
1309 
1318  protected function _formatErrorMessage($errorCode, $shortErrorMessage, $longErrorMessage)
1319  {
1320  $longErrorMessage = preg_replace('/\.$/', '', $longErrorMessage);
1321  $shortErrorMessage = preg_replace('/\.$/', '', $shortErrorMessage);
1322 
1323  return $longErrorMessage ? sprintf('%s (#%s: %s).', $longErrorMessage, $errorCode, $shortErrorMessage)
1324  : sprintf('#%s: %s.', $errorCode, $shortErrorMessage);
1325  }
1326 
1333  protected function _isProcessableError($errorCode)
1334  {
1335  $processableErrorsList = $this->getProcessableErrors();
1336 
1337  if (!$processableErrorsList || !is_array($processableErrorsList)) {
1338  return false;
1339  }
1340 
1341  return in_array($errorCode, $processableErrorsList);
1342  }
1343 
1351  {
1352  $errors = [];
1353 
1354  for ($i = 0; isset($response["L_ERRORCODE{$i}"]); $i++) {
1355  $errorCode = $response["L_ERRORCODE{$i}"];
1356  $errorMessage = $this->_formatErrorMessage(
1357  $errorCode,
1358  $response["L_SHORTMESSAGE{$i}"],
1359  isset($response["L_LONGMESSAGE{$i}"]) ? $response["L_LONGMESSAGE{$i}"] : null
1360  );
1361  $errors[] = [
1362  'code' => $errorCode,
1363  'message' => $errorMessage,
1364  ];
1365  }
1366 
1367  return $errors;
1368  }
1369 
1376  protected function _isCallSuccessful($response)
1377  {
1378  if (!isset($response['ACK'])) {
1379  return false;
1380  }
1381 
1382  $ack = strtoupper($response['ACK']);
1383  $this->_callWarnings = [];
1384  if ($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING') {
1385  // collect warnings
1386  if ($ack == 'SUCCESSWITHWARNING') {
1387  for ($i = 0; isset($response["L_ERRORCODE{$i}"]); $i++) {
1388  $this->_callWarnings[] = $response["L_ERRORCODE{$i}"];
1389  }
1390  }
1391  return true;
1392  }
1393  return false;
1394  }
1395 
1403  protected function _validateResponse($method, $response)
1404  {
1405  if (isset($this->_requiredResponseParams[$method])) {
1406  foreach ($this->_requiredResponseParams[$method] as $param) {
1407  if (!isset($response[$param])) {
1408  return false;
1409  }
1410  }
1411  }
1412  return true;
1413  }
1414 
1420  protected function _deformatNVP($nvpstr)
1421  {
1422  $intial = 0;
1423  $nvpArray = [];
1424 
1425  $nvpstr = strpos($nvpstr, "\r\n\r\n") !== false ? substr($nvpstr, strpos($nvpstr, "\r\n\r\n") + 4) : $nvpstr;
1426 
1427  while (strlen($nvpstr)) {
1428  //position of Key
1429  $keypos = strpos($nvpstr, '=');
1430  //position of value
1431  $valuepos = strpos($nvpstr, '&') ? strpos($nvpstr, '&') : strlen($nvpstr);
1432 
1433  /*getting the Key and Value values and storing in a Associative Array*/
1434  $keyval = substr($nvpstr, $intial, $keypos);
1435  $valval = substr($nvpstr, $keypos + 1, $valuepos - $keypos - 1);
1436  //decoding the response
1437  $nvpArray[urldecode($keyval)] = urldecode($valval);
1438  $nvpstr = substr($nvpstr, $valuepos + 1, strlen($nvpstr));
1439  }
1440  return $nvpArray;
1441  }
1442 
1450  protected function _exportLineItems(array &$request, $i = 0)
1451  {
1452  if (!$this->_cart) {
1453  return;
1454  }
1455  $this->_cart->setTransferDiscountAsItem();
1456  return parent::_exportLineItems($request, $i);
1457  }
1458 
1467  protected function _exportAddressses($data)
1468  {
1469  $this->_exportAddresses($data);
1470  }
1471 
1478  protected function _exportAddresses($data)
1479  {
1480  $address = new \Magento\Framework\DataObject();
1481  \Magento\Framework\DataObject\Mapper::accumulateByMap($data, $address, $this->_billingAddressMap);
1482  $address->setExportedKeys(array_values($this->_billingAddressMap));
1484  $this->setExportedBillingAddress($address);
1485  // assume there is shipping address if there is at least one field specific to shipping
1486  if (isset($data['SHIPTONAME'])) {
1487  $shippingAddress = clone $address;
1488  \Magento\Framework\DataObject\Mapper::accumulateByMap($data, $shippingAddress, $this->_shippingAddressMap);
1490  // PayPal doesn't provide detailed shipping name fields, so the name will be overwritten
1491  $shippingAddress->addData(['firstname' => $data['SHIPTONAME']]);
1492  $this->setExportedShippingAddress($shippingAddress);
1493  }
1494  }
1495 
1503  {
1504  // merge street addresses into 1
1505  if ($address->getData('street2') !== null) {
1506  $address->setStreet(implode("\n", [$address->getData('street'), $address->getData('street2')]));
1507  $address->unsetData('street2');
1508  }
1509  // attempt to fetch region_id from directory
1510  if ($address->getCountryId() && $address->getRegion()) {
1511  $regions = $this->_countryFactory->create()->loadByCode(
1512  $address->getCountryId()
1513  )->getRegionCollection()->addRegionCodeOrNameFilter(
1514  $address->getRegion()
1515  )->setPageSize(
1516  1
1517  );
1518  foreach ($regions as $region) {
1519  $address->setRegionId($region->getId());
1520  $address->setExportedKeys(array_merge($address->getExportedKeys(), ['region_id']));
1521  break;
1522  }
1523  }
1524  }
1525 
1532  protected function _importAddresses(array $to)
1533  {
1534  $billingAddress = $this->getBillingAddress() ? $this->getBillingAddress() : $this->getAddress();
1535  $shippingAddress = $this->getAddress();
1536 
1537  $to = \Magento\Framework\DataObject\Mapper::accumulateByMap(
1539  $to,
1540  array_merge(array_flip($this->_billingAddressMap), $this->_billingAddressMapRequest)
1541  );
1542  $regionCode = $this->_lookupRegionCodeFromAddress($billingAddress);
1543  if ($regionCode) {
1544  $to['STATE'] = $regionCode;
1545  }
1546  if (!$this->getSuppressShipping()) {
1547  $to = \Magento\Framework\DataObject\Mapper::accumulateByMap(
1549  $to,
1550  array_flip($this->_shippingAddressMap)
1551  );
1552  $regionCode = $this->_lookupRegionCodeFromAddress($shippingAddress);
1553  if ($regionCode) {
1554  $to['SHIPTOSTATE'] = $regionCode;
1555  }
1556  $this->_importStreetFromAddress($shippingAddress, $to, 'SHIPTOSTREET', 'SHIPTOSTREET2');
1557  $this->_importStreetFromAddress($billingAddress, $to, 'STREET', 'STREET2');
1558  $to['SHIPTONAME'] = $shippingAddress->getName();
1559  }
1560  return $to;
1561  }
1562 
1569  protected function _filterCcType($value)
1570  {
1571  if (isset($this->_supportedCcTypes[$value])) {
1572  return $this->_supportedCcTypes[$value];
1573  }
1574  return '';
1575  }
1576 
1583  protected function _filterToBool($value)
1584  {
1585  if ('false' === $value || '0' === $value) {
1586  return false;
1587  } elseif ('true' === $value || '1' === $value) {
1588  return true;
1589  }
1590  return $value;
1591  }
1592 
1599  protected function _filterBillFailedLater($value)
1600  {
1601  return $value ? 'AddToNextBilling' : 'NoAutoBill';
1602  }
1603 
1610  protected function _filterPeriodUnit($value)
1611  {
1612  switch ($value) {
1613  case 'day':
1614  return 'Day';
1615  case 'week':
1616  return 'Week';
1617  case 'semi_month':
1618  return 'SemiMonth';
1619  case 'month':
1620  return 'Month';
1621  case 'year':
1622  return 'Year';
1623  default:
1624  break;
1625  }
1626  }
1627 
1635  {
1636  return $value ? 'ContinueOnFailure' : 'CancelOnFailure';
1637  }
1638 
1646  {
1647  switch ($value) {
1648  case 'canceled':
1649  return 'Canceled';
1650  case 'active':
1651  return 'Active';
1652  default:
1653  break;
1654  }
1655  }
1656 
1665  {
1666  switch ($value) {
1667  case 'None':
1668  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_NONE;
1669  case 'Completed':
1670  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_COMPLETED;
1671  case 'Denied':
1672  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_DENIED;
1673  case 'Expired':
1674  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_EXPIRED;
1675  case 'Failed':
1676  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_FAILED;
1677  case 'In-Progress':
1678  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_INPROGRESS;
1679  case 'Pending':
1680  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_PENDING;
1681  case 'Refunded':
1682  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_REFUNDED;
1683  case 'Partially-Refunded':
1684  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_REFUNDEDPART;
1685  case 'Reversed':
1686  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_REVERSED;
1687  case 'Canceled-Reversal':
1688  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_UNREVERSED;
1689  case 'Processed':
1690  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_PROCESSED;
1691  case 'Voided':
1692  return \Magento\Paypal\Model\Info::PAYMENTSTATUS_VOIDED;
1693  default:
1694  break;
1695  }
1696  }
1697 
1705  {
1706  switch ($value) {
1707  case \Magento\Paypal\Model\Pro::PAYMENT_REVIEW_ACCEPT:
1708  return 'Accept';
1709  case \Magento\Paypal\Model\Pro::PAYMENT_REVIEW_DENY:
1710  return 'Deny';
1711  default:
1712  break;
1713  }
1714  }
1715 
1721  protected function _getCaptureCompleteType()
1722  {
1723  return $this->getIsCaptureComplete() ? $this->_captureTypeComplete : $this->_captureTypeNotcomplete;
1724  }
1725 
1732  protected function _prepareEachCallRequest($methodName)
1733  {
1734  $expressCheckoutMethods = [
1738  ];
1739  if (!in_array($methodName, $expressCheckoutMethods) || !$this->_config->shouldUseUnilateralPayments()) {
1740  return $this->_eachCallRequest;
1741  }
1742  return array_diff($this->_eachCallRequest, ['USER', 'PWD', 'SIGNATURE']);
1743  }
1744 
1751  protected function _prepareExpressCheckoutCallRequest(&$requestFields)
1752  {
1753  if (!$this->_config->shouldUseUnilateralPayments()) {
1754  $key = array_search('SUBJECT', $requestFields);
1755  if ($key) {
1756  unset($requestFields[$key]);
1757  }
1758  }
1759  }
1760 }
_exportShippingOptions(array &$request, $i=0)
$response
Definition: 404.php:11
prepareShippingOptionsCallbackAddress(array $request)
Definition: Nvp.php:1090
_exportLineItems(array &$request, $i=0)
Definition: Nvp.php:1450
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$billingAddress
Definition: order.php:25
_filterBillingAgreementStatus($value)
Definition: Nvp.php:1645
_extractErrorsFromResponse($response)
Definition: Nvp.php:1350
$config
Definition: fraud_order.php:17
_lookupRegionCodeFromAddress(\Magento\Framework\DataObject $address)
_isProcessableError($errorCode)
Definition: Nvp.php:1333
& _exportToRequest(array $privateRequestMap, array $request=[])
$shippingAddress
Definition: order.php:40
const PENDING_TRANSACTION_ACCEPT
Definition: Nvp.php:47
__()
Definition: __.php:13
_prepareExpressCheckoutCallRequest(&$requestFields)
Definition: Nvp.php:1751
$logger
$address
Definition: customer.php:38
_applyStreetAndRegionWorkarounds(\Magento\Framework\DataObject $address)
Definition: Nvp.php:1502
$value
Definition: gender.phtml:16
_importStreetFromAddress(\Magento\Framework\DataObject $address, array &$to)
_filterPaymentStatusFromNvpToInfo($value)
Definition: Nvp.php:1664
_validateResponse($method, $response)
Definition: Nvp.php:1403
_postProcessResponse($response)
Definition: Nvp.php:1134
$pos
Definition: list.phtml:42
_formatErrorMessage($errorCode, $shortErrorMessage, $longErrorMessage)
Definition: Nvp.php:1318
const DO_EXPRESS_CHECKOUT_PAYMENT
Definition: Nvp.php:40
_prepareEachCallRequest($methodName)
Definition: Nvp.php:1732
_importAddresses(array $to)
Definition: Nvp.php:1532
_addMethodToRequest($methodName, $request)
Definition: Nvp.php:1121
$method
Definition: info.phtml:13
_importFromResponse(array $privateResponseMap, array $response)
const GET_EXPRESS_CHECKOUT_DETAILS
Definition: Nvp.php:38
call($methodName, array $request)
Definition: Nvp.php:1163
_filterPaymentReviewAction($value)
Definition: Nvp.php:1704
_filterInitialAmountMayFail($value)
Definition: Nvp.php:1634
_isCallSuccessful($response)
Definition: Nvp.php:1376
const PENDING_TRANSACTION_DENY
Definition: Nvp.php:49
$i
Definition: gallery.phtml:31
__construct(\Magento\Customer\Helper\Address $customerAddress, \Psr\Log\LoggerInterface $logger, Logger $customLogger, \Magento\Framework\Locale\ResolverInterface $localeResolver, \Magento\Directory\Model\RegionFactory $regionFactory, \Magento\Directory\Model\CountryFactory $countryFactory, \Magento\Paypal\Model\Api\ProcessableExceptionFactory $processableExceptionFactory, \Magento\Framework\Exception\LocalizedExceptionFactory $frameworkExceptionFactory, \Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory, array $data=[])
Definition: Nvp.php:752
_filterBillFailedLater($value)
Definition: Nvp.php:1599
$errors
Definition: overview.phtml:9
$customerAddress