PluginProbe
PayPlug for WooCommerce (Official) / 3.0.0
PayPlug for WooCommerce (Official) v3.0.0
3.0.0 2.18.0 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.0 1.10.0 1.10.1 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 All 101 releases
payplug / src / Controller / PayplugGenericGateway.php

PayplugGenericGateway.php in PayPlug for WooCommerce (Official) 3.0.0, at src/Controller/PayplugGenericGateway.php

550 lines 20.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Payplug\PayplugWoocommerce\Controller;
4
5 use Payplug\Exception\HttpException;
6 use Payplug\Payplug;
7 use Payplug\PayplugWoocommerce\Gateway\PayplugAddressData;
8 use Payplug\PayplugWoocommerce\Gateway\PayplugGateway;
9 use Payplug\PayplugWoocommerce\Interfaces\PayplugGatewayBuilder;
10 use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper;
11 use Payplug\Resource\Payment as PaymentResource;
12 use Payplug\Resource\Refund as RefundResource;
13
14 class PayplugGenericGateway extends PayplugGateway implements PayplugGatewayBuilder
15 {
16 /**
17 * @var string
18 */
19 public $image;
20
21 /**
22 * @var array
23 */
24 protected $allowed_country_codes;
25
26 public function __construct()
27 {
28 parent::__construct();
29 add_action('woocommerce_after_order_itemmeta', [$this, 'hide_wc_refund_button'], 10, 3);
30 }
31
32 /**
33 * Generic code to fetch payment gateway specific image
34 *
35 * @return string
36 */
37 public function get_icon()
38 {
39
40 //get object $image
41 $icons = apply_filters('payplug_payment_icons', [
42 'payplug' => sprintf('<img src="%s" alt="%s" class="payplug-payment-icon" />', esc_url(PAYPLUG_GATEWAY_PLUGIN_URL . '/assets/images/checkout/' . $this->image), $this->id . ' Icon'),
43 ]);
44
45 $icons_str = '';
46 foreach ($icons as $icon) {
47 $icons_str .= $icon;
48 }
49
50 return $icons_str;
51 }
52
53 /**
54 * @return void
55 */
56 public function display_notice(): void
57 {
58 $error_message = 'payplug_' . $this->id . '_unauthorized_message'; ?>
59 <div class="notice notice-error is-dismissible">
60 <p><?php echo __($error_message, 'payplug'); ?></p>
61 </div>
62 <?php
63 }
64
65 public function checkGateway()
66 {
67 //check if module is enabled
68 if (!isset($this->settings['enabled']) || !$this->settings['enabled']) {
69 return false;
70 }
71
72 // todo: delete this usage to avoid call to getAccount resource each checkout loading
73 $account = PayplugWoocommerceHelper::generic_get_account_data_from_options($this->id);
74 $options = PayplugWoocommerceHelper::get_payplug_options();
75
76 if (!$this->check_api_gateway_enable($account)) {
77 return false;
78 }
79
80 if (!isset($options['payment_methods']) || empty($options['payment_methods'])) {
81 return false;
82 }
83
84 if (!$options['payment_methods']['configuration'][$this->id]['active']) {
85 return false;
86 }
87
88 $is_order_pay = is_wc_endpoint_url('order-pay');
89 $country_code_billing = null;
90 if (!is_admin() && (!PayplugWoocommerceHelper::is_checkout_block() || $is_order_pay)) {
91 if (empty(WC()->cart) && !is_admin() && !$is_order_pay) {
92 return false;
93 }
94
95 //for backend orders
96 if (!empty(get_query_var('order-pay'))) {
97 $order = wc_get_order((int) get_query_var('order-pay'));
98 $items = $order->get_items();
99 $country_code_billing = $order->get_billing_country();
100 // Skip cart population for subscription renewals: WC Subscriptions manages the cart
101 // itself and adding items here would cause a double entry in the order summary.
102 $is_renewal = function_exists('wcs_order_contains_renewal') && wcs_order_contains_renewal($order);
103 if (!$is_renewal) {
104 if (is_null(WC()->cart)) {
105 wc_load_cart();
106 }
107 $this->order_items_to_cart(WC()->cart, $items);
108 }
109 }
110
111 $source_before_fallback = $country_code_billing;
112 if (empty($country_code_billing)) {
113 $country_code_billing = method_exists(WC()->customer, 'get_billing_country') ? WC()->customer->get_billing_country() : null;
114 }
115
116 $permission_result = $this->check_billing_country_permissions($account, $country_code_billing);
117
118 if (!$permission_result) {
119 return false;
120 }
121 }
122
123 return true;
124 }
125
126 /**
127 * check if payment is enable and customer has permissions
128 *
129 * @param $account
130 *
131 * @return bool
132 */
133 protected function check_api_gateway_enable($account)
134 {
135 if (
136 isset($account['payment_methods']) &&
137 empty($account['payment_methods'][$this->id]) &&
138 !$account['payment_methods'][$this->id]['enabled']
139 ) {
140 return false;
141 }
142
143 return true;
144 }
145
146 /**
147 * Check if billing country can use this payment method
148 *
149 * @param $account
150 * @param $billing_code
151 *
152 * @return bool
153 */
154 public function check_billing_country_permissions($account, $billing_code)
155 {
156 if (!isset($account['payment_methods'][$this->id]['allowed_countries'])) {
157 return true;
158 }
159
160 $this->allowed_country_codes = $account['payment_methods'][$this->id]['allowed_countries'];
161
162 if (is_array($this->allowed_country_codes)) {
163 if (in_array('ALL', $this->allowed_country_codes) || empty($this->allowed_country_codes)) {
164 return true;
165 }
166
167 //check if country is allowed
168 if (in_array($billing_code, $this->allowed_country_codes)) {
169 return true;
170 } else {
171 return false;
172 }
173 }
174
175 return false;
176 }
177
178 /**
179 * if payment was generated by an intend, we shouldn't generate another one and try to pay it, this would generate duplications
180 *
181 * @param $order
182 *
183 * @throws \Exception
184 *
185 * @return array|null
186 */
187 private function process_standard_intent_payment($order)
188 {
189 $embedded_mode = $this->settings['payment_methods']['configuration']['payplug']['embedded_mode'];
190
191 // This can run from AJAX endpoints whose own request URL never carries the order-pay
192 // query var, so is_wc_endpoint_url() alone can't detect that context here: fall back
193 // to the order_key/order_pay_key sent by the order-pay AJAX flows.
194 $is_order_pay = $this->is_order_pay_request($order);
195
196 if (!$is_order_pay &&
197 empty($_POST['payplug_non_blocks']) &&
198 PayplugWoocommerceHelper::is_checkout_block() &&
199 (
200 ($this->id === 'payplug' && in_array($embedded_mode, ['integrated', 'popup'])) ||
201 ($this->id === 'american_express' && 'popup' == $embedded_mode)
202 ) &&
203 !empty($order->get_transaction_id())) {
204 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
205
206 try {
207 $payment = $this->payplug_api->payment_retrieve($order->get_transaction_id());
208 if (ob_get_length() > 0) {
209 ob_clean();
210 }
211
212 // Save transaction id for the order
213 PayplugWoocommerceHelper::is_pre_30()
214 ? update_post_meta($order_id, '_transaction_id', $payment->id)
215 : $order->set_transaction_id($payment->id);
216
217 if (is_callable([$order, 'save'])) {
218 $order->save();
219 }
220
221 /**
222 * Fires once a payment has been created.
223 *
224 * @param int $order_id Order ID
225 * @param PaymentResource $payment Payment resource
226 */
227 \do_action('payplug_gateway_payment_created', $order_id, $payment);
228
229 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
230 PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
231
232 PayplugGateway::log(sprintf('Payment intent created for order #%s', $order_id));
233
234 $return_url = esc_url_raw($order->get_checkout_order_received_url());
235
236 $result = [
237 'payment_id' => $payment->id,
238 'result' => 'success',
239 'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
240 'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null,
241 ];
242
243 // wp_send_json_success() calls die(), which is only safe for the classic
244 // wc-ajax request this was written for: the Store API checkout flow (used by
245 // the checkout block) calls process_payment() through the REST framework,
246 // and killing the process mid-request there produces a broken response.
247 if (wp_doing_ajax()) {
248 wp_send_json_success($result);
249 }
250
251 return $result;
252 } catch (HttpException $e) {
253 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
254 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
255 } catch (\Exception $e) {
256 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
257 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
258 }
259 }
260
261 return null;
262 }
263
264 public function process_standard_payment($order, $amount, $customer_id)
265 {
266 $intent = $this->process_standard_intent_payment($order);
267 if (!empty($intent)) {
268 return $intent;
269 }
270
271 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
272
273 try {
274
275 //if there's no auth to process payment
276 if (!$this->checkGateway()) {
277 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
278 }
279
280 $address_data = PayplugAddressData::from_order($order);
281 $return_url = esc_url_raw($order->get_checkout_order_received_url());
282
283 if (!(substr($return_url, 0, 4) === 'http')) {
284 $return_url = get_site_url() . $return_url;
285 }
286 $payment_data = [
287 'amount' => $amount,
288 'currency' => get_woocommerce_currency(),
289 'payment_method' => $this->id,
290 'billing' => $address_data->get_billing(),
291 'shipping' => $address_data->get_shipping(),
292 'hosted_payment' => [
293 'return_url' => $return_url,
294 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
295 ],
296 'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
297 'metadata' => [
298 'order_id' => $order_id,
299 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
300 'domain' => $this->limit_length(esc_url_raw(home_url()), 500),
301 ],
302 'save_card' => false,
303 'force_3ds' => false,
304 ];
305 if (!empty($payment_data['billing']['landline_phone_number'])) {
306 $payment_data['billing']['mobile_phone_number'] = $payment_data['billing']['landline_phone_number'];
307 }
308
309 if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) {
310 $payment_data['metadata']['woocommerce_block'] = 'CHECKOUT';
311 } elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) {
312 $payment_data['metadata']['woocommerce_block'] = 'CART';
313 }
314 /**
315 * Filter the payment data before it's used
316 *
317 * @param array $payment_data
318 * @param int $order_id
319 * @param array $customer_details
320 * @param PayplugAddressData $address_data
321 */
322 $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);
323 $payment = $this->payplug_api->payment_create($payment_data);
324
325 // Save transaction id for the order
326 PayplugWoocommerceHelper::is_pre_30()
327 ? update_post_meta($order_id, '_transaction_id', $payment->id)
328 : $order->set_transaction_id($payment->id);
329
330 $order->set_payment_method($this->id);
331 $order->set_payment_method_title($this->method_title);
332
333 if (is_callable([$order, 'save'])) {
334 $order->save();
335 }
336
337 /**
338 * Fires once a payment has been created.
339 *
340 * @param int $order_id Order ID
341 * @param PaymentResource $payment Payment resource
342 */
343 \do_action('payplug_gateway_payment_created', $order_id, $payment);
344
345 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
346 PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
347
348 PayplugGateway::log(sprintf('Payment creation complete for order #%s', $order_id));
349
350 return [
351 'result' => 'success',
352 'redirect' => $payment->hosted_payment->payment_url,
353 'cancel' => $payment->hosted_payment->cancel_url,
354 ];
355 } catch (HttpException $e) {
356 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
357 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
358 } catch (\Exception $e) {
359 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
360 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
361 }
362 }
363
364 /**
365 * Process refund for an order paid with PayPlug gateway.
366 *
367 * @param int $order_id
368 * @param null $amount
369 * @param string $reason
370 *
371 * @return bool|\WP_Error
372 */
373 public function process_refund($order_id, $amount = null, $reason = '')
374 {
375 PayplugGateway::log(sprintf('Processing refund for order #%s', $order_id));
376
377 if (!$this->user_logged_in()) {
378 PayplugGateway::log(__('You must be logged in with your PayPlug account.', 'payplug'), 'error');
379
380 return new \WP_Error('process_refund_error', __('You must be logged in with your PayPlug account.', 'payplug'));
381 }
382
383 $order = wc_get_order($order_id);
384 if (!$order instanceof \WC_Order) {
385 PayplugGateway::log(sprintf('The order #%s does not exist.', $order_id), 'error');
386
387 return new \WP_Error('process_refund_error', sprintf(__('The order %s does not exist.', 'payplug'), $order_id));
388 }
389
390 if ($order->get_status() === 'cancelled') {
391 PayplugGateway::log(sprintf('The order #%s cannot be refund.', $order_id), 'error');
392
393 return new \WP_Error('process_refund_error', sprintf(__('The order %s cannot be refund.', 'payplug'), $order_id));
394 }
395
396 $transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta($order_id, '_transaction_id', true) : $order->get_transaction_id();
397 if (empty($transaction_id)) {
398 PayplugGateway::log(sprintf('The order #%s does not have PayPlug transaction ID associated with it.', $order_id), 'error');
399
400 return new \WP_Error('process_refund_error', __('No PayPlug transaction was found for this order. The refund could not be processed.', 'payplug'));
401 }
402
403 /**
404 * PPRO gateways feature!
405 */
406 if (isset($this->enable_refund) && $this->enable_refund === false) {
407 PayplugGateway::log(__('payplug_refund_disabled_error', 'payplug'), 'error');
408
409 return new \WP_Error('process_refund_error', __('payplug_refund_disabled_error', 'payplug'));
410 }
411
412 $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
413 $data = [
414 'metadata' => [
415 'order_id' => $order_id,
416 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
417 'refund_from' => 'woocommerce',
418 ],
419 ];
420
421 if (!is_null($amount)) {
422 $data['amount'] = PayplugWoocommerceHelper::get_payplug_amount($amount);
423 }
424
425 if (!empty($reason)) {
426 $data['metadata']['reason'] = $reason;
427 }
428
429 /**
430 * Filter the refund data before it's used.
431 *
432 * @param array $data
433 * @param int $order_id
434 * @param string $transaction_id
435 */
436 $data = apply_filters('payplug_gateway_refund_data', $data, $order_id, $transaction_id);
437
438 try {
439 $refund = $this->payplug_api->refund_create($transaction_id, $data);
440
441 if ($refund->object === 'error') {
442 PayplugGateway::log(__('payplug_ppro_flag_error', 'payplug'), 'error');
443
444 return new \WP_Error('process_refund_error', __('payplug_ppro_flag_error', 'payplug'));
445 }
446
447 /**
448 * Fires once a refund has been created.
449 *
450 * @param int $order_id Order ID
451 * @param RefundResource $refund Refund resource
452 * @param string $transaction_id Transaction id
453 */
454 \do_action('payplug_gateway_refund_created', $order_id, $refund, $transaction_id);
455
456 $refund_meta_key = sprintf('_pr_%s', wc_clean($refund->id));
457 if (PayplugWoocommerceHelper::is_pre_30()) {
458 update_post_meta($order_id, $refund_meta_key, $refund->id);
459 } else {
460 $order->add_meta_data($refund_meta_key, $refund->id, true);
461 $order->save();
462 }
463
464 $note = sprintf(__('Refund %s : Refunded %s', 'payplug'), wc_clean($refund->id), wc_price(((int) $refund->amount) / 100));
465 if (!empty($refund->metadata['reason'])) {
466 $note .= sprintf(' (%s)', esc_html($refund->metadata['reason']));
467 }
468 $order->add_order_note($note);
469
470 try {
471 $payment = $this->payplug_api->payment_retrieve($transaction_id);
472 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
473 PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
474 } catch (\Exception $e) {
475 }
476
477 PayplugGateway::log('Refund process complete for the order.');
478
479 return true;
480 } catch (HttpException $e) {
481 PayplugGateway::log(sprintf('Refund request error for the order %s from PayPlug API : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
482
483 return new \WP_Error('process_refund_error', __('The transaction could not be refunded. Please try again.', 'payplug'));
484 } catch (\Exception $e) {
485 PayplugGateway::log(sprintf('Refund request error for the order %s : %s', $order_id, wc_clean($e->getMessage())), 'error');
486
487 return new \WP_Error('process_refund_error', __('The transaction could not be refunded. Please try again.', 'payplug'));
488 }
489 }
490
491 /**
492 * Avoid usage of button refund on the BO
493 *
494 * @param int $item_id
495 * @param \WC_Order_Item|null $item
496 * @param mixed $product
497 *
498 * @return false|void
499 */
500 public function hide_wc_refund_button($item_id, $item = null, $product = null)
501 {
502 if (!$item instanceof \WC_Order_Item) {
503 return false;
504 }
505
506 $order = $item->get_order();
507 if (!$order instanceof \WC_Order) {
508 return false;
509 }
510
511 if ($order->get_payment_method() === $this->id && isset($this->enable_refund) && $this->enable_refund === false) {
512 ?>
513 <script>
514 jQuery(function () {
515 jQuery('.refund-items').attr("disabled", true);
516 });
517 </script>
518 <?php
519 }
520 }
521
522 /**
523 * refund not possible for PPRO payments
524 *
525 * @return void
526 */
527 public function refund_not_available($order): void
528 {
529 if ($this->id === $order->get_payment_method() && isset($this->enable_refund) && $this->enable_refund === false) {
530 echo "<p style='color: red;'>" . __('payplug_refund_disabled_error', 'payplug') . '</p>';
531 }
532 }
533
534 /**
535 * Empty the cart and add all order_items
536 *
537 * @param $cart
538 * @param $items
539 *
540 * @return void
541 */
542 private function order_items_to_cart($cart, $items): void
543 {
544 $cart->empty_cart();
545 foreach ($items as $item) {
546 $cart->add_to_cart($item->get_product_id(), $item->get_quantity(), $item->get_variation_id());
547 }
548 }
549 }
550