Completed
Branch ENH/11418/ppex-http-build-quer... (1ef9fe)
by
unknown
13:09 queued 14s
created
payment_methods/Paypal_Express/EEG_Paypal_Express.gateway.php 1 patch
Indentation   +671 added lines, -671 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('NO direct script access allowed');
2
+	exit('NO direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -16,680 +16,680 @@  discard block
 block discarded – undo
16 16
  */
17 17
 //Quickfix to address https://events.codebasehq.com/projects/event-espresso/tickets/11089 ASAP
18 18
 if (! function_exists('mb_strcut')) {
19
-    /**
20
-     * Very simple mimic of mb_substr (which WP ensures exists in wp-includes/compat.php). Still has all the problems of mb_substr
21
-     * (namely, that we might send too many characters to PayPal; however in this case they just issue a warning but nothing breaks)
22
-     * @param $string
23
-     * @param $start
24
-     * @param $length
25
-     * @return bool|string
26
-     */
27
-    function mb_strcut($string, $start, $length = null)
28
-    {
29
-        return mb_substr($string, $start, $length);
30
-    }
19
+	/**
20
+	 * Very simple mimic of mb_substr (which WP ensures exists in wp-includes/compat.php). Still has all the problems of mb_substr
21
+	 * (namely, that we might send too many characters to PayPal; however in this case they just issue a warning but nothing breaks)
22
+	 * @param $string
23
+	 * @param $start
24
+	 * @param $length
25
+	 * @return bool|string
26
+	 */
27
+	function mb_strcut($string, $start, $length = null)
28
+	{
29
+		return mb_substr($string, $start, $length);
30
+	}
31 31
 }
32 32
 class EEG_Paypal_Express extends EE_Offsite_Gateway
33 33
 {
34 34
 
35
-    /**
36
-     * Merchant API Username.
37
-     *
38
-     * @var string
39
-     */
40
-    protected $_api_username;
41
-
42
-    /**
43
-     * Merchant API Password.
44
-     *
45
-     * @var string
46
-     */
47
-    protected $_api_password;
48
-
49
-    /**
50
-     * API Signature.
51
-     *
52
-     * @var string
53
-     */
54
-    protected $_api_signature;
55
-
56
-    /**
57
-     * Request Shipping address on PP checkout page.
58
-     *
59
-     * @var string
60
-     */
61
-    protected $_request_shipping_addr;
62
-
63
-    /**
64
-     * Business/personal logo.
65
-     *
66
-     * @var string
67
-     */
68
-    protected $_image_url;
69
-
70
-    /**
71
-     * gateway URL variable
72
-     *
73
-     * @var string
74
-     */
75
-    protected $_base_gateway_url = '';
76
-
77
-
78
-
79
-    /**
80
-     * EEG_Paypal_Express constructor.
81
-     */
82
-    public function __construct()
83
-    {
84
-        $this->_currencies_supported = array(
85
-            'USD',
86
-            'AUD',
87
-            'BRL',
88
-            'CAD',
89
-            'CZK',
90
-            'DKK',
91
-            'EUR',
92
-            'HKD',
93
-            'HUF',
94
-            'ILS',
95
-            'JPY',
96
-            'MYR',
97
-            'MXN',
98
-            'NOK',
99
-            'NZD',
100
-            'PHP',
101
-            'PLN',
102
-            'GBP',
103
-            'RUB',
104
-            'SGD',
105
-            'SEK',
106
-            'CHF',
107
-            'TWD',
108
-            'THB',
109
-            'TRY',
110
-        );
111
-        parent::__construct();
112
-    }
113
-
114
-
115
-
116
-    /**
117
-     * Sets the gateway URL variable based on whether debug mode is enabled or not.
118
-     *
119
-     * @param array $settings_array
120
-     */
121
-    public function set_settings($settings_array)
122
-    {
123
-        parent::set_settings($settings_array);
124
-        // Redirect URL.
125
-        $this->_base_gateway_url = $this->_debug_mode
126
-            ? 'https://api-3t.sandbox.paypal.com/nvp'
127
-            : 'https://api-3t.paypal.com/nvp';
128
-    }
129
-
130
-
131
-
132
-    /**
133
-     * @param EEI_Payment $payment
134
-     * @param array       $billing_info
135
-     * @param string      $return_url
136
-     * @param string      $notify_url
137
-     * @param string      $cancel_url
138
-     * @return \EE_Payment|\EEI_Payment
139
-     * @throws \EE_Error
140
-     */
141
-    public function set_redirection_info(
142
-        $payment,
143
-        $billing_info = array(),
144
-        $return_url = null,
145
-        $notify_url = null,
146
-        $cancel_url = null
147
-    ) {
148
-        if (! $payment instanceof EEI_Payment) {
149
-            $payment->set_gateway_response(
150
-                esc_html__(
151
-                    'Error. No associated payment was found.',
152
-                    'event_espresso'
153
-                )
154
-            );
155
-            $payment->set_status($this->_pay_model->failed_status());
156
-            return $payment;
157
-        }
158
-        $transaction = $payment->transaction();
159
-        if (! $transaction instanceof EEI_Transaction) {
160
-            $payment->set_gateway_response(
161
-                esc_html__(
162
-                    'Could not process this payment because it has no associated transaction.',
163
-                    'event_espresso'
164
-                )
165
-            );
166
-            $payment->set_status($this->_pay_model->failed_status());
167
-            return $payment;
168
-        }
169
-        $gateway_formatter = $this->_get_gateway_formatter();
170
-        $order_description = mb_strcut($gateway_formatter->formatOrderDescription($payment), 0, 127);
171
-        $primary_registration = $transaction->primary_registration();
172
-        $primary_attendee = $primary_registration instanceof EE_Registration
173
-            ? $primary_registration->attendee()
174
-            : false;
175
-        $locale = explode('-', get_bloginfo('language'));
176
-        // Gather request parameters.
177
-        $token_request_dtls = array(
178
-            'METHOD'                         => 'SetExpressCheckout',
179
-            'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
180
-            'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
181
-            'PAYMENTREQUEST_0_DESC'          => $order_description,
182
-            'RETURNURL'                      => $return_url,
183
-            'CANCELURL'                      => $cancel_url,
184
-            'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
185
-            // Buyer does not need to create a PayPal account to check out.
186
-            // This is referred to as PayPal Account Optional.
187
-            'SOLUTIONTYPE'                   => 'Sole',
188
-            //EE will blow up if you change this
189
-            'BUTTONSOURCE'                   => 'EventEspresso_SP',
190
-            // Locale of the pages displayed by PayPal during Express Checkout.
191
-            'LOCALECODE'                     => $locale[1]
192
-        );
193
-        // Show itemized list.
194
-        $itemized_list = $this->itemize_list($payment, $transaction);
195
-        $token_request_dtls = array_merge($token_request_dtls, $itemized_list);
196
-        // Automatically filling out shipping and contact information.
197
-        if ($this->_request_shipping_addr && $primary_attendee instanceof EEI_Attendee) {
198
-            // If you do not pass the shipping address, PayPal obtains it from the buyer's account profile.
199
-            $token_request_dtls['NOSHIPPING'] = '2';
200
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET'] = $primary_attendee->address();
201
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET2'] = $primary_attendee->address2();
202
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOCITY'] = $primary_attendee->city();
203
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTATE'] = $primary_attendee->state_abbrev();
204
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'] = $primary_attendee->country_ID();
205
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOZIP'] = $primary_attendee->zip();
206
-            $token_request_dtls['PAYMENTREQUEST_0_EMAIL'] = $primary_attendee->email();
207
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOPHONENUM'] = $primary_attendee->phone();
208
-        } elseif (! $this->_request_shipping_addr) {
209
-            // Do not request shipping details on the PP Checkout page.
210
-            $token_request_dtls['NOSHIPPING'] = '1';
211
-            $token_request_dtls['REQCONFIRMSHIPPING'] = '0';
212
-        }
213
-        // Used a business/personal logo on the PayPal page.
214
-        if (! empty($this->_image_url)) {
215
-            $token_request_dtls['LOGOIMG'] = $this->_image_url;
216
-        }
217
-        $token_request_dtls = apply_filters(
218
-            'FHEE__EEG_Paypal_Express__set_redirection_info__arguments',
219
-            $token_request_dtls,
220
-            $this
221
-        );
222
-        // Request PayPal token.
223
-        $token_request_response = $this->_ppExpress_request($token_request_dtls, 'Payment Token', $payment);
224
-        $token_rstatus = $this->_ppExpress_check_response($token_request_response);
225
-        $response_args = (isset($token_rstatus['args']) && is_array($token_rstatus['args']))
226
-            ? $token_rstatus['args']
227
-            : array();
228
-        if ($token_rstatus['status']) {
229
-            // We got the Token so we may continue with the payment and redirect the client.
230
-            $payment->set_details($response_args);
231
-            $gateway_url = $this->_debug_mode ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
232
-            $payment->set_redirect_url(
233
-                $gateway_url
234
-                . '/checkoutnow?useraction=commit&cmd=_express-checkout&token='
235
-                . $response_args['TOKEN']
236
-            );
237
-        } else {
238
-            if (isset($response_args['L_ERRORCODE'])) {
239
-                $payment->set_gateway_response($response_args['L_ERRORCODE'] . '; ' . $response_args['L_SHORTMESSAGE']);
240
-            } else {
241
-                $payment->set_gateway_response(
242
-                    esc_html__(
243
-                        'Error occurred while trying to setup the Express Checkout.',
244
-                        'event_espresso'
245
-                    )
246
-                );
247
-            }
248
-            $payment->set_details($response_args);
249
-            $payment->set_status($this->_pay_model->failed_status());
250
-        }
251
-        return $payment;
252
-    }
253
-
254
-
255
-
256
-    /**
257
-     * @param array           $update_info {
258
-     * @type string           $gateway_txn_id
259
-     * @type string status an EEMI_Payment status
260
-     *                                     }
261
-     * @param EEI_Transaction $transaction
262
-     * @return EEI_Payment
263
-     */
264
-    public function handle_payment_update($update_info, $transaction)
265
-    {
266
-        $payment = $transaction instanceof EEI_Transaction ? $transaction->last_payment() : null;
267
-        if ($payment instanceof EEI_Payment) {
268
-            $this->log(array('Return from Authorization' => $update_info), $payment);
269
-            $transaction = $payment->transaction();
270
-            if (! $transaction instanceof EEI_Transaction) {
271
-                $payment->set_gateway_response(
272
-                    esc_html__(
273
-                        'Could not process this payment because it has no associated transaction.',
274
-                        'event_espresso'
275
-                    )
276
-                );
277
-                $payment->set_status($this->_pay_model->failed_status());
278
-                return $payment;
279
-            }
280
-            $primary_registrant = $transaction->primary_registration();
281
-            $payment_details = $payment->details();
282
-            // Check if we still have the token.
283
-            if (! isset($payment_details['TOKEN']) || empty($payment_details['TOKEN'])) {
284
-                $payment->set_status($this->_pay_model->failed_status());
285
-                return $payment;
286
-            }
287
-            $cdetails_request_dtls = array(
288
-                'METHOD' => 'GetExpressCheckoutDetails',
289
-                'TOKEN'  => $payment_details['TOKEN'],
290
-            );
291
-            // Request Customer Details.
292
-            $cdetails_request_response = $this->_ppExpress_request(
293
-                $cdetails_request_dtls,
294
-                'Customer Details',
295
-                $payment
296
-            );
297
-            $cdetails_rstatus = $this->_ppExpress_check_response($cdetails_request_response);
298
-            $cdata_response_args = (isset($cdetails_rstatus['args']) && is_array($cdetails_rstatus['args']))
299
-                ? $cdetails_rstatus['args']
300
-                : array();
301
-            if ($cdetails_rstatus['status']) {
302
-                // We got the PayerID so now we can Complete the transaction.
303
-                $docheckout_request_dtls = array(
304
-                    'METHOD'                         => 'DoExpressCheckoutPayment',
305
-                    'PAYERID'                        => $cdata_response_args['PAYERID'],
306
-                    'TOKEN'                          => $payment_details['TOKEN'],
307
-                    'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
308
-                    'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
309
-                    'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
310
-                    //EE will blow up if you change this
311
-                    'BUTTONSOURCE'                   => 'EventEspresso_SP',
312
-                );
313
-                 // Include itemized list.
314
-                $itemized_list = $this->itemize_list(
315
-                    $payment,
316
-                    $transaction,
317
-                    $cdata_response_args
318
-                );
319
-                $docheckout_request_dtls = array_merge($docheckout_request_dtls, $itemized_list);
320
-                // Payment Checkout/Capture.
321
-                $docheckout_request_response = $this->_ppExpress_request(
322
-                    $docheckout_request_dtls,
323
-                    'Do Payment',
324
-                    $payment
325
-                );
326
-                $docheckout_rstatus = $this->_ppExpress_check_response($docheckout_request_response);
327
-                $docheckout_response_args = (isset($docheckout_rstatus['args']) && is_array($docheckout_rstatus['args']))
328
-                    ? $docheckout_rstatus['args']
329
-                    : array();
330
-                if ($docheckout_rstatus['status']) {
331
-                    // All is well, payment approved.
332
-                    $primary_registration_code = $primary_registrant instanceof EE_Registration ?
333
-                        $primary_registrant->reg_code()
334
-                        : '';
335
-                    $payment->set_extra_accntng($primary_registration_code);
336
-                    $payment->set_amount(isset($docheckout_response_args['PAYMENTINFO_0_AMT'])
337
-                        ? (float)$docheckout_response_args['PAYMENTINFO_0_AMT']
338
-                        : 0);
339
-                    $payment->set_txn_id_chq_nmbr(isset($docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID'])
340
-                        ? $docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID']
341
-                        : null);
342
-                    $payment->set_details($cdata_response_args);
343
-                    $payment->set_gateway_response(isset($docheckout_response_args['PAYMENTINFO_0_ACK'])
344
-                        ? $docheckout_response_args['PAYMENTINFO_0_ACK']
345
-                        : '');
346
-                    $payment->set_status($this->_pay_model->approved_status());
347
-                } else {
348
-                    if (isset($docheckout_response_args['L_ERRORCODE'])) {
349
-                        $payment->set_gateway_response(
350
-                            $docheckout_response_args['L_ERRORCODE']
351
-                            . '; '
352
-                            . $docheckout_response_args['L_SHORTMESSAGE']
353
-                        );
354
-                    } else {
355
-                        $payment->set_gateway_response(
356
-                            esc_html__(
357
-                                'Error occurred while trying to Capture the funds.',
358
-                                'event_espresso'
359
-                            )
360
-                        );
361
-                    }
362
-                    $payment->set_details($docheckout_response_args);
363
-                    $payment->set_status($this->_pay_model->declined_status());
364
-                }
365
-            } else {
366
-                if (isset($cdata_response_args['L_ERRORCODE'])) {
367
-                    $payment->set_gateway_response(
368
-                        $cdata_response_args['L_ERRORCODE']
369
-                        . '; '
370
-                        . $cdata_response_args['L_SHORTMESSAGE']
371
-                    );
372
-                } else {
373
-                    $payment->set_gateway_response(
374
-                        esc_html__(
375
-                            'Error occurred while trying to get payment Details from PayPal.',
376
-                            'event_espresso'
377
-                        )
378
-                    );
379
-                }
380
-                $payment->set_details($cdata_response_args);
381
-                $payment->set_status($this->_pay_model->failed_status());
382
-            }
383
-        } else {
384
-            $payment->set_gateway_response(
385
-                esc_html__(
386
-                    'Error occurred while trying to process the payment.',
387
-                    'event_espresso'
388
-                )
389
-            );
390
-            $payment->set_status($this->_pay_model->failed_status());
391
-        }
392
-        return $payment;
393
-    }
394
-
395
-
396
-
397
-    /**
398
-     *  Make a list of items that are in the giver transaction.
399
-     *
400
-     * @param EEI_Payment     $payment
401
-     * @param EEI_Transaction $transaction
402
-     * @param array           $request_response_args Data from a previous communication with PP.
403
-     * @return array
404
-     */
405
-    public function itemize_list(EEI_Payment $payment, EEI_Transaction $transaction, $request_response_args = array())
406
-    {
407
-        $itemized_list = array();
408
-        $gateway_formatter = $this->_get_gateway_formatter();
409
-        // If we have data from a previous communication with PP (on this transaction) we may use that for our list...
410
-        if (
411
-            ! empty($request_response_args)
412
-            && array_key_exists('L_PAYMENTREQUEST_0_AMT0', $request_response_args)
413
-            && array_key_exists('PAYMENTREQUEST_0_ITEMAMT', $request_response_args)
414
-        ) {
415
-            foreach ($request_response_args as $arg_key => $arg_val) {
416
-                if (
417
-                    strpos($arg_key, 'PAYMENTREQUEST_') !== false
418
-                    && strpos($arg_key, 'NOTIFYURL') === false
419
-                ) {
420
-                    $itemized_list[$arg_key] = $arg_val;
421
-                }
422
-            }
423
-            // If we got only a few Items then something is not right.
424
-            if (count($itemized_list) > 2) {
425
-                return $itemized_list;
426
-            } else {
427
-                if (WP_DEBUG) {
428
-                    throw new EE_Error(
429
-                        sprintf(
430
-                            esc_html__(
431
-                                // @codingStandardsIgnoreStart
432
-                                'Unable to continue with the checkout because a proper purchase list could not be generated. The purchased list we could have sent was %1$s',
433
-                                // @codingStandardsIgnoreEnd
434
-                                'event_espresso'
435
-                            ),
436
-                            wp_json_encode($itemized_list)
437
-                        )
438
-                    );
439
-                }
440
-                // Reset the list and log an error, maybe allow to try and generate a new list (below).
441
-                $itemized_list = array();
442
-                $this->log(
443
-                    array(
444
-                        esc_html__(
445
-                            'Could not generate a proper item list with:',
446
-                            'event_espresso'
447
-                        ) => $request_response_args
448
-                    ),
449
-                    $payment
450
-                );
451
-            }
452
-        }
453
-        // ...otherwise we generate a new list for this transaction.
454
-        if ($this->_money->compare_floats($payment->amount(), $transaction->total(), '==')) {
455
-            $item_num = 0;
456
-            $itemized_sum = 0;
457
-            $total_line_items = $transaction->total_line_item();
458
-            // Go through each item in the list.
459
-            foreach ($total_line_items->get_items() as $line_item) {
460
-                if ($line_item instanceof EE_Line_Item) {
461
-                    // PayPal doesn't like line items with 0.00 amount, so we may skip those.
462
-                    if (EEH_Money::compare_floats($line_item->total(), '0.00', '==')) {
463
-                        continue;
464
-                    }
465
-                    $unit_price = $line_item->unit_price();
466
-                    $line_item_quantity = $line_item->quantity();
467
-                    // This is a discount.
468
-                    if ($line_item->is_percent()) {
469
-                        $unit_price = $line_item->total();
470
-                        $line_item_quantity = 1;
471
-                    }
472
-                    // Item Name.
473
-                    $itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
474
-                        $gateway_formatter->formatLineItemName($line_item, $payment),
475
-                        0,
476
-                        127
477
-                    );
478
-                    // Item description.
479
-                    $itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = mb_strcut(
480
-                        $gateway_formatter->formatLineItemDesc($line_item, $payment),
481
-                        0,
482
-                        127
483
-                    );
484
-                    // Cost of individual item.
485
-                    $itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $gateway_formatter->formatCurrency($unit_price);
486
-                    // Item Number.
487
-                    $itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
488
-                    // Item quantity.
489
-                    $itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = $line_item_quantity;
490
-                    // Digital item is sold.
491
-                    $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
492
-                    $itemized_sum += $line_item->total();
493
-                    ++$item_num;
494
-                }
495
-            }
496
-            // Item's sales S/H and tax amount.
497
-            $itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $total_line_items->get_items_total();
498
-            $itemized_list['PAYMENTREQUEST_0_TAXAMT'] = $total_line_items->get_total_tax();
499
-            $itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
500
-            $itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
501
-            $itemized_sum_diff_from_txn_total = round(
502
-                $transaction->total() - $itemized_sum - $total_line_items->get_total_tax(),
503
-                2
504
-            );
505
-            // If we were not able to recognize some item like promotion, surcharge or cancellation,
506
-            // add the difference as an extra line item.
507
-            if ($this->_money->compare_floats($itemized_sum_diff_from_txn_total, 0, '!=')) {
508
-                // Item Name.
509
-                $itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
510
-                    esc_html__(
511
-                        'Other (promotion/surcharge/cancellation)',
512
-                        'event_espresso'
513
-                    ),
514
-                    0,
515
-                    127
516
-                );
517
-                // Item description.
518
-                $itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = '';
519
-                // Cost of individual item.
520
-                $itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $gateway_formatter->formatCurrency(
521
-                    $itemized_sum_diff_from_txn_total
522
-                );
523
-                // Item Number.
524
-                $itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
525
-                // Item quantity.
526
-                $itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = 1;
527
-                // Digital item is sold.
528
-                $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
529
-                $item_num++;
530
-            }
531
-        } else {
532
-            // Just one Item.
533
-            // Item Name.
534
-            $itemized_list['L_PAYMENTREQUEST_0_NAME0'] = mb_strcut(
535
-                $gateway_formatter->formatPartialPaymentLineItemName($payment),
536
-                0,
537
-                127
538
-            );
539
-            // Item description.
540
-            $itemized_list['L_PAYMENTREQUEST_0_DESC0'] = mb_strcut(
541
-                $gateway_formatter->formatPartialPaymentLineItemDesc($payment),
542
-                0,
543
-                127
544
-            );
545
-            // Cost of individual item.
546
-            $itemized_list['L_PAYMENTREQUEST_0_AMT0'] = $gateway_formatter->formatCurrency($payment->amount());
547
-            // Item Number.
548
-            $itemized_list['L_PAYMENTREQUEST_0_NUMBER0'] = 1;
549
-            // Item quantity.
550
-            $itemized_list['L_PAYMENTREQUEST_0_QTY0'] = 1;
551
-            // Digital item is sold.
552
-            $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY0'] = 'Physical';
553
-            // Item's sales S/H and tax amount.
554
-            $itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $gateway_formatter->formatCurrency($payment->amount());
555
-            $itemized_list['PAYMENTREQUEST_0_TAXAMT'] = '0';
556
-            $itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
557
-            $itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
558
-        }
559
-        return $itemized_list;
560
-    }
561
-
562
-
563
-
564
-    /**
565
-     *  Make the Express checkout request.
566
-     *
567
-     * @param array       $request_params
568
-     * @param string      $request_text
569
-     * @param EEI_Payment $payment
570
-     * @return mixed
571
-     */
572
-    public function _ppExpress_request($request_params, $request_text, $payment)
573
-    {
574
-        $request_dtls = array(
575
-            'VERSION'   => '204.0',
576
-            'USER'      => urlencode($this->_api_username),
577
-            'PWD'       => urlencode($this->_api_password),
578
-            'SIGNATURE' => urlencode($this->_api_signature),
579
-        );
580
-        $dtls = array_merge($request_dtls, $request_params);
581
-        $this->_log_clean_request($dtls, $payment, $request_text . ' Request');
582
-        // Request Customer Details.
583
-        $request_response = wp_remote_post(
584
-            $this->_base_gateway_url,
585
-            array(
586
-                'method'      => 'POST',
587
-                'timeout'     => 45,
588
-                'httpversion' => '1.1',
589
-                'cookies'     => array(),
590
-                'headers'     => array(),
591
-                'body'        => http_build_query($dtls, '', '&'),
592
-            )
593
-        );
594
-        // Log the response.
595
-        $this->log(array($request_text . ' Response' => $request_response), $payment);
596
-        return $request_response;
597
-    }
598
-
599
-
600
-
601
-    /**
602
-     *  Check the response status.
603
-     *
604
-     * @param mixed $request_response
605
-     * @return array
606
-     */
607
-    public function _ppExpress_check_response($request_response)
608
-    {
609
-        if (is_wp_error($request_response) || empty($request_response['body'])) {
610
-            // If we got here then there was an error in this request.
611
-            return array('status' => false, 'args' => $request_response);
612
-        }
613
-        $response_args = array();
614
-        parse_str(urldecode($request_response['body']), $response_args);
615
-        if (! isset($response_args['ACK'])) {
616
-            return array('status' => false, 'args' => $request_response);
617
-        }
618
-        if (
619
-            (
620
-                isset($response_args['PAYERID'])
621
-                || isset($response_args['TOKEN'])
622
-                || isset($response_args['PAYMENTINFO_0_TRANSACTIONID'])
623
-                || (isset($response_args['PAYMENTSTATUS']) && $response_args['PAYMENTSTATUS'] === 'Completed')
624
-            )
625
-            && in_array($response_args['ACK'], array('Success', 'SuccessWithWarning'), true)
626
-        ) {
627
-            // Response status OK, return response parameters for further processing.
628
-            return array('status' => true, 'args' => $response_args);
629
-        }
630
-        $errors = $this->_get_errors($response_args);
631
-        return array('status' => false, 'args' => $errors);
632
-    }
633
-
634
-
635
-
636
-    /**
637
-     *  Log a "Cleared" request.
638
-     *
639
-     * @param array       $request
640
-     * @param EEI_Payment $payment
641
-     * @param string      $info
642
-     * @return void
643
-     */
644
-    private function _log_clean_request($request, $payment, $info)
645
-    {
646
-        $cleaned_request_data = $request;
647
-        unset($cleaned_request_data['PWD'], $cleaned_request_data['USER'], $cleaned_request_data['SIGNATURE']);
648
-        $this->log(array($info => $cleaned_request_data), $payment);
649
-    }
650
-
651
-
652
-
653
-    /**
654
-     *  Get error from the response data.
655
-     *
656
-     * @param array $data_array
657
-     * @return array
658
-     */
659
-    private function _get_errors($data_array)
660
-    {
661
-        $errors = array();
662
-        $n = 0;
663
-        while (isset($data_array["L_ERRORCODE{$n}"])) {
664
-            $l_error_code = isset($data_array["L_ERRORCODE{$n}"])
665
-                ? $data_array["L_ERRORCODE{$n}"]
666
-                : '';
667
-            $l_severity_code = isset($data_array["L_SEVERITYCODE{$n}"])
668
-                ? $data_array["L_SEVERITYCODE{$n}"]
669
-                : '';
670
-            $l_short_message = isset($data_array["L_SHORTMESSAGE{$n}"])
671
-                ? $data_array["L_SHORTMESSAGE{$n}"]
672
-                : '';
673
-            $l_long_message = isset($data_array["L_LONGMESSAGE{$n}"])
674
-                ? $data_array["L_LONGMESSAGE{$n}"]
675
-                : '';
676
-            if ($n === 0) {
677
-                $errors = array(
678
-                    'L_ERRORCODE'    => $l_error_code,
679
-                    'L_SHORTMESSAGE' => $l_short_message,
680
-                    'L_LONGMESSAGE'  => $l_long_message,
681
-                    'L_SEVERITYCODE' => $l_severity_code,
682
-                );
683
-            } else {
684
-                $errors['L_ERRORCODE'] .= ', ' . $l_error_code;
685
-                $errors['L_SHORTMESSAGE'] .= ', ' . $l_short_message;
686
-                $errors['L_LONGMESSAGE'] .= ', ' . $l_long_message;
687
-                $errors['L_SEVERITYCODE'] .= ', ' . $l_severity_code;
688
-            }
689
-            $n++;
690
-        }
691
-        return $errors;
692
-    }
35
+	/**
36
+	 * Merchant API Username.
37
+	 *
38
+	 * @var string
39
+	 */
40
+	protected $_api_username;
41
+
42
+	/**
43
+	 * Merchant API Password.
44
+	 *
45
+	 * @var string
46
+	 */
47
+	protected $_api_password;
48
+
49
+	/**
50
+	 * API Signature.
51
+	 *
52
+	 * @var string
53
+	 */
54
+	protected $_api_signature;
55
+
56
+	/**
57
+	 * Request Shipping address on PP checkout page.
58
+	 *
59
+	 * @var string
60
+	 */
61
+	protected $_request_shipping_addr;
62
+
63
+	/**
64
+	 * Business/personal logo.
65
+	 *
66
+	 * @var string
67
+	 */
68
+	protected $_image_url;
69
+
70
+	/**
71
+	 * gateway URL variable
72
+	 *
73
+	 * @var string
74
+	 */
75
+	protected $_base_gateway_url = '';
76
+
77
+
78
+
79
+	/**
80
+	 * EEG_Paypal_Express constructor.
81
+	 */
82
+	public function __construct()
83
+	{
84
+		$this->_currencies_supported = array(
85
+			'USD',
86
+			'AUD',
87
+			'BRL',
88
+			'CAD',
89
+			'CZK',
90
+			'DKK',
91
+			'EUR',
92
+			'HKD',
93
+			'HUF',
94
+			'ILS',
95
+			'JPY',
96
+			'MYR',
97
+			'MXN',
98
+			'NOK',
99
+			'NZD',
100
+			'PHP',
101
+			'PLN',
102
+			'GBP',
103
+			'RUB',
104
+			'SGD',
105
+			'SEK',
106
+			'CHF',
107
+			'TWD',
108
+			'THB',
109
+			'TRY',
110
+		);
111
+		parent::__construct();
112
+	}
113
+
114
+
115
+
116
+	/**
117
+	 * Sets the gateway URL variable based on whether debug mode is enabled or not.
118
+	 *
119
+	 * @param array $settings_array
120
+	 */
121
+	public function set_settings($settings_array)
122
+	{
123
+		parent::set_settings($settings_array);
124
+		// Redirect URL.
125
+		$this->_base_gateway_url = $this->_debug_mode
126
+			? 'https://api-3t.sandbox.paypal.com/nvp'
127
+			: 'https://api-3t.paypal.com/nvp';
128
+	}
129
+
130
+
131
+
132
+	/**
133
+	 * @param EEI_Payment $payment
134
+	 * @param array       $billing_info
135
+	 * @param string      $return_url
136
+	 * @param string      $notify_url
137
+	 * @param string      $cancel_url
138
+	 * @return \EE_Payment|\EEI_Payment
139
+	 * @throws \EE_Error
140
+	 */
141
+	public function set_redirection_info(
142
+		$payment,
143
+		$billing_info = array(),
144
+		$return_url = null,
145
+		$notify_url = null,
146
+		$cancel_url = null
147
+	) {
148
+		if (! $payment instanceof EEI_Payment) {
149
+			$payment->set_gateway_response(
150
+				esc_html__(
151
+					'Error. No associated payment was found.',
152
+					'event_espresso'
153
+				)
154
+			);
155
+			$payment->set_status($this->_pay_model->failed_status());
156
+			return $payment;
157
+		}
158
+		$transaction = $payment->transaction();
159
+		if (! $transaction instanceof EEI_Transaction) {
160
+			$payment->set_gateway_response(
161
+				esc_html__(
162
+					'Could not process this payment because it has no associated transaction.',
163
+					'event_espresso'
164
+				)
165
+			);
166
+			$payment->set_status($this->_pay_model->failed_status());
167
+			return $payment;
168
+		}
169
+		$gateway_formatter = $this->_get_gateway_formatter();
170
+		$order_description = mb_strcut($gateway_formatter->formatOrderDescription($payment), 0, 127);
171
+		$primary_registration = $transaction->primary_registration();
172
+		$primary_attendee = $primary_registration instanceof EE_Registration
173
+			? $primary_registration->attendee()
174
+			: false;
175
+		$locale = explode('-', get_bloginfo('language'));
176
+		// Gather request parameters.
177
+		$token_request_dtls = array(
178
+			'METHOD'                         => 'SetExpressCheckout',
179
+			'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
180
+			'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
181
+			'PAYMENTREQUEST_0_DESC'          => $order_description,
182
+			'RETURNURL'                      => $return_url,
183
+			'CANCELURL'                      => $cancel_url,
184
+			'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
185
+			// Buyer does not need to create a PayPal account to check out.
186
+			// This is referred to as PayPal Account Optional.
187
+			'SOLUTIONTYPE'                   => 'Sole',
188
+			//EE will blow up if you change this
189
+			'BUTTONSOURCE'                   => 'EventEspresso_SP',
190
+			// Locale of the pages displayed by PayPal during Express Checkout.
191
+			'LOCALECODE'                     => $locale[1]
192
+		);
193
+		// Show itemized list.
194
+		$itemized_list = $this->itemize_list($payment, $transaction);
195
+		$token_request_dtls = array_merge($token_request_dtls, $itemized_list);
196
+		// Automatically filling out shipping and contact information.
197
+		if ($this->_request_shipping_addr && $primary_attendee instanceof EEI_Attendee) {
198
+			// If you do not pass the shipping address, PayPal obtains it from the buyer's account profile.
199
+			$token_request_dtls['NOSHIPPING'] = '2';
200
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET'] = $primary_attendee->address();
201
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET2'] = $primary_attendee->address2();
202
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOCITY'] = $primary_attendee->city();
203
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTATE'] = $primary_attendee->state_abbrev();
204
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'] = $primary_attendee->country_ID();
205
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOZIP'] = $primary_attendee->zip();
206
+			$token_request_dtls['PAYMENTREQUEST_0_EMAIL'] = $primary_attendee->email();
207
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOPHONENUM'] = $primary_attendee->phone();
208
+		} elseif (! $this->_request_shipping_addr) {
209
+			// Do not request shipping details on the PP Checkout page.
210
+			$token_request_dtls['NOSHIPPING'] = '1';
211
+			$token_request_dtls['REQCONFIRMSHIPPING'] = '0';
212
+		}
213
+		// Used a business/personal logo on the PayPal page.
214
+		if (! empty($this->_image_url)) {
215
+			$token_request_dtls['LOGOIMG'] = $this->_image_url;
216
+		}
217
+		$token_request_dtls = apply_filters(
218
+			'FHEE__EEG_Paypal_Express__set_redirection_info__arguments',
219
+			$token_request_dtls,
220
+			$this
221
+		);
222
+		// Request PayPal token.
223
+		$token_request_response = $this->_ppExpress_request($token_request_dtls, 'Payment Token', $payment);
224
+		$token_rstatus = $this->_ppExpress_check_response($token_request_response);
225
+		$response_args = (isset($token_rstatus['args']) && is_array($token_rstatus['args']))
226
+			? $token_rstatus['args']
227
+			: array();
228
+		if ($token_rstatus['status']) {
229
+			// We got the Token so we may continue with the payment and redirect the client.
230
+			$payment->set_details($response_args);
231
+			$gateway_url = $this->_debug_mode ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
232
+			$payment->set_redirect_url(
233
+				$gateway_url
234
+				. '/checkoutnow?useraction=commit&cmd=_express-checkout&token='
235
+				. $response_args['TOKEN']
236
+			);
237
+		} else {
238
+			if (isset($response_args['L_ERRORCODE'])) {
239
+				$payment->set_gateway_response($response_args['L_ERRORCODE'] . '; ' . $response_args['L_SHORTMESSAGE']);
240
+			} else {
241
+				$payment->set_gateway_response(
242
+					esc_html__(
243
+						'Error occurred while trying to setup the Express Checkout.',
244
+						'event_espresso'
245
+					)
246
+				);
247
+			}
248
+			$payment->set_details($response_args);
249
+			$payment->set_status($this->_pay_model->failed_status());
250
+		}
251
+		return $payment;
252
+	}
253
+
254
+
255
+
256
+	/**
257
+	 * @param array           $update_info {
258
+	 * @type string           $gateway_txn_id
259
+	 * @type string status an EEMI_Payment status
260
+	 *                                     }
261
+	 * @param EEI_Transaction $transaction
262
+	 * @return EEI_Payment
263
+	 */
264
+	public function handle_payment_update($update_info, $transaction)
265
+	{
266
+		$payment = $transaction instanceof EEI_Transaction ? $transaction->last_payment() : null;
267
+		if ($payment instanceof EEI_Payment) {
268
+			$this->log(array('Return from Authorization' => $update_info), $payment);
269
+			$transaction = $payment->transaction();
270
+			if (! $transaction instanceof EEI_Transaction) {
271
+				$payment->set_gateway_response(
272
+					esc_html__(
273
+						'Could not process this payment because it has no associated transaction.',
274
+						'event_espresso'
275
+					)
276
+				);
277
+				$payment->set_status($this->_pay_model->failed_status());
278
+				return $payment;
279
+			}
280
+			$primary_registrant = $transaction->primary_registration();
281
+			$payment_details = $payment->details();
282
+			// Check if we still have the token.
283
+			if (! isset($payment_details['TOKEN']) || empty($payment_details['TOKEN'])) {
284
+				$payment->set_status($this->_pay_model->failed_status());
285
+				return $payment;
286
+			}
287
+			$cdetails_request_dtls = array(
288
+				'METHOD' => 'GetExpressCheckoutDetails',
289
+				'TOKEN'  => $payment_details['TOKEN'],
290
+			);
291
+			// Request Customer Details.
292
+			$cdetails_request_response = $this->_ppExpress_request(
293
+				$cdetails_request_dtls,
294
+				'Customer Details',
295
+				$payment
296
+			);
297
+			$cdetails_rstatus = $this->_ppExpress_check_response($cdetails_request_response);
298
+			$cdata_response_args = (isset($cdetails_rstatus['args']) && is_array($cdetails_rstatus['args']))
299
+				? $cdetails_rstatus['args']
300
+				: array();
301
+			if ($cdetails_rstatus['status']) {
302
+				// We got the PayerID so now we can Complete the transaction.
303
+				$docheckout_request_dtls = array(
304
+					'METHOD'                         => 'DoExpressCheckoutPayment',
305
+					'PAYERID'                        => $cdata_response_args['PAYERID'],
306
+					'TOKEN'                          => $payment_details['TOKEN'],
307
+					'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
308
+					'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
309
+					'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
310
+					//EE will blow up if you change this
311
+					'BUTTONSOURCE'                   => 'EventEspresso_SP',
312
+				);
313
+				 // Include itemized list.
314
+				$itemized_list = $this->itemize_list(
315
+					$payment,
316
+					$transaction,
317
+					$cdata_response_args
318
+				);
319
+				$docheckout_request_dtls = array_merge($docheckout_request_dtls, $itemized_list);
320
+				// Payment Checkout/Capture.
321
+				$docheckout_request_response = $this->_ppExpress_request(
322
+					$docheckout_request_dtls,
323
+					'Do Payment',
324
+					$payment
325
+				);
326
+				$docheckout_rstatus = $this->_ppExpress_check_response($docheckout_request_response);
327
+				$docheckout_response_args = (isset($docheckout_rstatus['args']) && is_array($docheckout_rstatus['args']))
328
+					? $docheckout_rstatus['args']
329
+					: array();
330
+				if ($docheckout_rstatus['status']) {
331
+					// All is well, payment approved.
332
+					$primary_registration_code = $primary_registrant instanceof EE_Registration ?
333
+						$primary_registrant->reg_code()
334
+						: '';
335
+					$payment->set_extra_accntng($primary_registration_code);
336
+					$payment->set_amount(isset($docheckout_response_args['PAYMENTINFO_0_AMT'])
337
+						? (float)$docheckout_response_args['PAYMENTINFO_0_AMT']
338
+						: 0);
339
+					$payment->set_txn_id_chq_nmbr(isset($docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID'])
340
+						? $docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID']
341
+						: null);
342
+					$payment->set_details($cdata_response_args);
343
+					$payment->set_gateway_response(isset($docheckout_response_args['PAYMENTINFO_0_ACK'])
344
+						? $docheckout_response_args['PAYMENTINFO_0_ACK']
345
+						: '');
346
+					$payment->set_status($this->_pay_model->approved_status());
347
+				} else {
348
+					if (isset($docheckout_response_args['L_ERRORCODE'])) {
349
+						$payment->set_gateway_response(
350
+							$docheckout_response_args['L_ERRORCODE']
351
+							. '; '
352
+							. $docheckout_response_args['L_SHORTMESSAGE']
353
+						);
354
+					} else {
355
+						$payment->set_gateway_response(
356
+							esc_html__(
357
+								'Error occurred while trying to Capture the funds.',
358
+								'event_espresso'
359
+							)
360
+						);
361
+					}
362
+					$payment->set_details($docheckout_response_args);
363
+					$payment->set_status($this->_pay_model->declined_status());
364
+				}
365
+			} else {
366
+				if (isset($cdata_response_args['L_ERRORCODE'])) {
367
+					$payment->set_gateway_response(
368
+						$cdata_response_args['L_ERRORCODE']
369
+						. '; '
370
+						. $cdata_response_args['L_SHORTMESSAGE']
371
+					);
372
+				} else {
373
+					$payment->set_gateway_response(
374
+						esc_html__(
375
+							'Error occurred while trying to get payment Details from PayPal.',
376
+							'event_espresso'
377
+						)
378
+					);
379
+				}
380
+				$payment->set_details($cdata_response_args);
381
+				$payment->set_status($this->_pay_model->failed_status());
382
+			}
383
+		} else {
384
+			$payment->set_gateway_response(
385
+				esc_html__(
386
+					'Error occurred while trying to process the payment.',
387
+					'event_espresso'
388
+				)
389
+			);
390
+			$payment->set_status($this->_pay_model->failed_status());
391
+		}
392
+		return $payment;
393
+	}
394
+
395
+
396
+
397
+	/**
398
+	 *  Make a list of items that are in the giver transaction.
399
+	 *
400
+	 * @param EEI_Payment     $payment
401
+	 * @param EEI_Transaction $transaction
402
+	 * @param array           $request_response_args Data from a previous communication with PP.
403
+	 * @return array
404
+	 */
405
+	public function itemize_list(EEI_Payment $payment, EEI_Transaction $transaction, $request_response_args = array())
406
+	{
407
+		$itemized_list = array();
408
+		$gateway_formatter = $this->_get_gateway_formatter();
409
+		// If we have data from a previous communication with PP (on this transaction) we may use that for our list...
410
+		if (
411
+			! empty($request_response_args)
412
+			&& array_key_exists('L_PAYMENTREQUEST_0_AMT0', $request_response_args)
413
+			&& array_key_exists('PAYMENTREQUEST_0_ITEMAMT', $request_response_args)
414
+		) {
415
+			foreach ($request_response_args as $arg_key => $arg_val) {
416
+				if (
417
+					strpos($arg_key, 'PAYMENTREQUEST_') !== false
418
+					&& strpos($arg_key, 'NOTIFYURL') === false
419
+				) {
420
+					$itemized_list[$arg_key] = $arg_val;
421
+				}
422
+			}
423
+			// If we got only a few Items then something is not right.
424
+			if (count($itemized_list) > 2) {
425
+				return $itemized_list;
426
+			} else {
427
+				if (WP_DEBUG) {
428
+					throw new EE_Error(
429
+						sprintf(
430
+							esc_html__(
431
+								// @codingStandardsIgnoreStart
432
+								'Unable to continue with the checkout because a proper purchase list could not be generated. The purchased list we could have sent was %1$s',
433
+								// @codingStandardsIgnoreEnd
434
+								'event_espresso'
435
+							),
436
+							wp_json_encode($itemized_list)
437
+						)
438
+					);
439
+				}
440
+				// Reset the list and log an error, maybe allow to try and generate a new list (below).
441
+				$itemized_list = array();
442
+				$this->log(
443
+					array(
444
+						esc_html__(
445
+							'Could not generate a proper item list with:',
446
+							'event_espresso'
447
+						) => $request_response_args
448
+					),
449
+					$payment
450
+				);
451
+			}
452
+		}
453
+		// ...otherwise we generate a new list for this transaction.
454
+		if ($this->_money->compare_floats($payment->amount(), $transaction->total(), '==')) {
455
+			$item_num = 0;
456
+			$itemized_sum = 0;
457
+			$total_line_items = $transaction->total_line_item();
458
+			// Go through each item in the list.
459
+			foreach ($total_line_items->get_items() as $line_item) {
460
+				if ($line_item instanceof EE_Line_Item) {
461
+					// PayPal doesn't like line items with 0.00 amount, so we may skip those.
462
+					if (EEH_Money::compare_floats($line_item->total(), '0.00', '==')) {
463
+						continue;
464
+					}
465
+					$unit_price = $line_item->unit_price();
466
+					$line_item_quantity = $line_item->quantity();
467
+					// This is a discount.
468
+					if ($line_item->is_percent()) {
469
+						$unit_price = $line_item->total();
470
+						$line_item_quantity = 1;
471
+					}
472
+					// Item Name.
473
+					$itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
474
+						$gateway_formatter->formatLineItemName($line_item, $payment),
475
+						0,
476
+						127
477
+					);
478
+					// Item description.
479
+					$itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = mb_strcut(
480
+						$gateway_formatter->formatLineItemDesc($line_item, $payment),
481
+						0,
482
+						127
483
+					);
484
+					// Cost of individual item.
485
+					$itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $gateway_formatter->formatCurrency($unit_price);
486
+					// Item Number.
487
+					$itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
488
+					// Item quantity.
489
+					$itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = $line_item_quantity;
490
+					// Digital item is sold.
491
+					$itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
492
+					$itemized_sum += $line_item->total();
493
+					++$item_num;
494
+				}
495
+			}
496
+			// Item's sales S/H and tax amount.
497
+			$itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $total_line_items->get_items_total();
498
+			$itemized_list['PAYMENTREQUEST_0_TAXAMT'] = $total_line_items->get_total_tax();
499
+			$itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
500
+			$itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
501
+			$itemized_sum_diff_from_txn_total = round(
502
+				$transaction->total() - $itemized_sum - $total_line_items->get_total_tax(),
503
+				2
504
+			);
505
+			// If we were not able to recognize some item like promotion, surcharge or cancellation,
506
+			// add the difference as an extra line item.
507
+			if ($this->_money->compare_floats($itemized_sum_diff_from_txn_total, 0, '!=')) {
508
+				// Item Name.
509
+				$itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
510
+					esc_html__(
511
+						'Other (promotion/surcharge/cancellation)',
512
+						'event_espresso'
513
+					),
514
+					0,
515
+					127
516
+				);
517
+				// Item description.
518
+				$itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = '';
519
+				// Cost of individual item.
520
+				$itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $gateway_formatter->formatCurrency(
521
+					$itemized_sum_diff_from_txn_total
522
+				);
523
+				// Item Number.
524
+				$itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
525
+				// Item quantity.
526
+				$itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = 1;
527
+				// Digital item is sold.
528
+				$itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
529
+				$item_num++;
530
+			}
531
+		} else {
532
+			// Just one Item.
533
+			// Item Name.
534
+			$itemized_list['L_PAYMENTREQUEST_0_NAME0'] = mb_strcut(
535
+				$gateway_formatter->formatPartialPaymentLineItemName($payment),
536
+				0,
537
+				127
538
+			);
539
+			// Item description.
540
+			$itemized_list['L_PAYMENTREQUEST_0_DESC0'] = mb_strcut(
541
+				$gateway_formatter->formatPartialPaymentLineItemDesc($payment),
542
+				0,
543
+				127
544
+			);
545
+			// Cost of individual item.
546
+			$itemized_list['L_PAYMENTREQUEST_0_AMT0'] = $gateway_formatter->formatCurrency($payment->amount());
547
+			// Item Number.
548
+			$itemized_list['L_PAYMENTREQUEST_0_NUMBER0'] = 1;
549
+			// Item quantity.
550
+			$itemized_list['L_PAYMENTREQUEST_0_QTY0'] = 1;
551
+			// Digital item is sold.
552
+			$itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY0'] = 'Physical';
553
+			// Item's sales S/H and tax amount.
554
+			$itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $gateway_formatter->formatCurrency($payment->amount());
555
+			$itemized_list['PAYMENTREQUEST_0_TAXAMT'] = '0';
556
+			$itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
557
+			$itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
558
+		}
559
+		return $itemized_list;
560
+	}
561
+
562
+
563
+
564
+	/**
565
+	 *  Make the Express checkout request.
566
+	 *
567
+	 * @param array       $request_params
568
+	 * @param string      $request_text
569
+	 * @param EEI_Payment $payment
570
+	 * @return mixed
571
+	 */
572
+	public function _ppExpress_request($request_params, $request_text, $payment)
573
+	{
574
+		$request_dtls = array(
575
+			'VERSION'   => '204.0',
576
+			'USER'      => urlencode($this->_api_username),
577
+			'PWD'       => urlencode($this->_api_password),
578
+			'SIGNATURE' => urlencode($this->_api_signature),
579
+		);
580
+		$dtls = array_merge($request_dtls, $request_params);
581
+		$this->_log_clean_request($dtls, $payment, $request_text . ' Request');
582
+		// Request Customer Details.
583
+		$request_response = wp_remote_post(
584
+			$this->_base_gateway_url,
585
+			array(
586
+				'method'      => 'POST',
587
+				'timeout'     => 45,
588
+				'httpversion' => '1.1',
589
+				'cookies'     => array(),
590
+				'headers'     => array(),
591
+				'body'        => http_build_query($dtls, '', '&'),
592
+			)
593
+		);
594
+		// Log the response.
595
+		$this->log(array($request_text . ' Response' => $request_response), $payment);
596
+		return $request_response;
597
+	}
598
+
599
+
600
+
601
+	/**
602
+	 *  Check the response status.
603
+	 *
604
+	 * @param mixed $request_response
605
+	 * @return array
606
+	 */
607
+	public function _ppExpress_check_response($request_response)
608
+	{
609
+		if (is_wp_error($request_response) || empty($request_response['body'])) {
610
+			// If we got here then there was an error in this request.
611
+			return array('status' => false, 'args' => $request_response);
612
+		}
613
+		$response_args = array();
614
+		parse_str(urldecode($request_response['body']), $response_args);
615
+		if (! isset($response_args['ACK'])) {
616
+			return array('status' => false, 'args' => $request_response);
617
+		}
618
+		if (
619
+			(
620
+				isset($response_args['PAYERID'])
621
+				|| isset($response_args['TOKEN'])
622
+				|| isset($response_args['PAYMENTINFO_0_TRANSACTIONID'])
623
+				|| (isset($response_args['PAYMENTSTATUS']) && $response_args['PAYMENTSTATUS'] === 'Completed')
624
+			)
625
+			&& in_array($response_args['ACK'], array('Success', 'SuccessWithWarning'), true)
626
+		) {
627
+			// Response status OK, return response parameters for further processing.
628
+			return array('status' => true, 'args' => $response_args);
629
+		}
630
+		$errors = $this->_get_errors($response_args);
631
+		return array('status' => false, 'args' => $errors);
632
+	}
633
+
634
+
635
+
636
+	/**
637
+	 *  Log a "Cleared" request.
638
+	 *
639
+	 * @param array       $request
640
+	 * @param EEI_Payment $payment
641
+	 * @param string      $info
642
+	 * @return void
643
+	 */
644
+	private function _log_clean_request($request, $payment, $info)
645
+	{
646
+		$cleaned_request_data = $request;
647
+		unset($cleaned_request_data['PWD'], $cleaned_request_data['USER'], $cleaned_request_data['SIGNATURE']);
648
+		$this->log(array($info => $cleaned_request_data), $payment);
649
+	}
650
+
651
+
652
+
653
+	/**
654
+	 *  Get error from the response data.
655
+	 *
656
+	 * @param array $data_array
657
+	 * @return array
658
+	 */
659
+	private function _get_errors($data_array)
660
+	{
661
+		$errors = array();
662
+		$n = 0;
663
+		while (isset($data_array["L_ERRORCODE{$n}"])) {
664
+			$l_error_code = isset($data_array["L_ERRORCODE{$n}"])
665
+				? $data_array["L_ERRORCODE{$n}"]
666
+				: '';
667
+			$l_severity_code = isset($data_array["L_SEVERITYCODE{$n}"])
668
+				? $data_array["L_SEVERITYCODE{$n}"]
669
+				: '';
670
+			$l_short_message = isset($data_array["L_SHORTMESSAGE{$n}"])
671
+				? $data_array["L_SHORTMESSAGE{$n}"]
672
+				: '';
673
+			$l_long_message = isset($data_array["L_LONGMESSAGE{$n}"])
674
+				? $data_array["L_LONGMESSAGE{$n}"]
675
+				: '';
676
+			if ($n === 0) {
677
+				$errors = array(
678
+					'L_ERRORCODE'    => $l_error_code,
679
+					'L_SHORTMESSAGE' => $l_short_message,
680
+					'L_LONGMESSAGE'  => $l_long_message,
681
+					'L_SEVERITYCODE' => $l_severity_code,
682
+				);
683
+			} else {
684
+				$errors['L_ERRORCODE'] .= ', ' . $l_error_code;
685
+				$errors['L_SHORTMESSAGE'] .= ', ' . $l_short_message;
686
+				$errors['L_LONGMESSAGE'] .= ', ' . $l_long_message;
687
+				$errors['L_SEVERITYCODE'] .= ', ' . $l_severity_code;
688
+			}
689
+			$n++;
690
+		}
691
+		return $errors;
692
+	}
693 693
 
694 694
 }
695 695
 // End of file EEG_Paypal_Express.gateway.php
Please login to merge, or discard this patch.