PluginProbe
PayPlug for WooCommerce (Official) / 3.1.0
PayPlug for WooCommerce (Official) v3.1.0
3.1.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 All 102 releases
← All changes | src/PayplugWoocommerceRequest.php +639 -77 1.10.0 → 3.1.0 View file →
@@ -2,109 +2,671 @@
2 2
3 3 namespace Payplug\PayplugWoocommerce;
4 4
5 5 // Exit if accessed directly
6 +use Automattic\WooCommerce\Utilities\OrderUtil;
7 +use Payplug\Exception\HttpException;
8 +use Payplug\PayplugWoocommerce\Gateway\PayplugAddressData;
6 9 use Payplug\PayplugWoocommerce\Gateway\PayplugGateway;
10 +use Payplug\PayplugWoocommerce\Traits\ServiceGetter;
7 11
8 -if ( ! defined( 'ABSPATH' ) ) {
9 - exit;
12 +if (!defined('ABSPATH')) {
13 + exit;
10 14 }
11 15
12 16 /**
13 17 * Class PayplugWoocommerceRequest
14 - * @package Payplug\PayplugWoocommerce
15 18 */
16 -class PayplugWoocommerceRequest {
19 +class PayplugWoocommerceRequest
20 +{
21 + use ServiceGetter;
17 22
18 - /**
19 - * Gateway settings.
20 - *
21 - * @var array
22 - */
23 - protected $settings;
23 + /**
24 + * Gateway settings.
25 + *
26 + * @var array
27 + */
28 + protected $settings;
24 29
25 - /**
26 - * PayplugWoocommerceRequest constructor.
27 - */
28 - public function __construct() {
29 - $this->settings = get_option( 'woocommerce_payplug_settings', [] );
30 - if ( empty( $this->settings ) || ! isset( $this->settings['enabled'] ) || 'yes' !== $this->settings['enabled'] ) {
31 - return;
32 - }
30 + /**
31 + * @var PayplugGateway
32 + */
33 + protected $gateway;
33 34
34 - // Don't load for change payment method page.
35 - if ( isset( $_GET['change_payment_method'] ) ) {
36 - return;
37 - }
35 + /**
36 + * PayplugWoocommerceRequest constructor.
37 + */
38 + public function __construct()
39 + {
40 + $this->settings = $this->get_configuration()->get_options();
38 41
39 - add_action( 'template_redirect', [ $this, 'set_session' ] );
40 - add_action( 'wc_ajax_payplug_create_order', [ $this, 'ajax_create_order' ] );
41 - add_action( 'wc_ajax_applepay_update_payment', [ $this, 'applepay_update_payment' ] );
42 - }
42 + if (empty($this->settings) || !isset($this->settings['enabled']) || !(bool) $this->settings['enabled']) {
43 + return;
44 + }
43 45
44 - /**
45 - * Sets the WC customer session if one is not set.
46 - * This is needed so nonces can be verified by AJAX Request.
47 - */
48 - public function set_session() {
49 - if ( ! is_product() || ( isset( WC()->session ) && WC()->session->has_session() ) ) {
50 - return;
51 - }
46 + // Don't load for change payment method page.
47 + if (isset($_GET['change_payment_method'])) {
48 + return;
49 + }
52 50
53 - $session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' );
54 - $wc_session = new $session_class();
51 + add_action('template_redirect', [$this, 'set_session']);
52 + add_action('wc_ajax_payplug_create_order', [$this, 'ajax_create_order']);
53 + add_action('wc_ajax_applepay_update_payment', [$this, 'applepay_update_payment']);
54 + add_action('wc_ajax_applepay_get_order_totals', [$this, 'applepay_get_order_totals']);
55 + add_action('wc_ajax_payplug_order_review_url', [$this, 'ajax_create_payment']);
56 + add_action('wc_ajax_payplug_apple_pay_create_order_pay', [$this, 'ajax_apple_pay_create_order_pay']);
57 + add_action('wc_ajax_payplug_check_payment', [$this, 'check_payment']);
58 + add_action('wc_ajax_payplug_create_intent', [$this, 'create_payment_intent']);
59 + }
55 60
56 - if ( version_compare( WC_VERSION, '3.3', '>=' ) ) {
57 - $wc_session->init();
58 - }
61 + /**
62 + * Sets the WC customer session if one is not set.
63 + * This is needed so nonces can be verified by AJAX Request.
64 + */
65 + public function set_session(): void
66 + {
67 + if (!is_product() || (isset(WC()->session) && WC()->session->has_session())) {
68 + return;
69 + }
59 70
60 - $wc_session->set_customer_session_cookie( true );
61 - }
71 + $session_class = apply_filters('woocommerce_session_handler', 'WC_Session_Handler');
72 + $wc_session = new $session_class();
62 73
63 - /**
64 - * Create the woocommerce order in the BO
65 - *
66 - */
67 - public function ajax_create_order() {
68 - if ( WC()->cart->is_empty() ) {
69 - wp_send_json_error( __( 'Empty cart', 'payplug' ) );
70 - }
74 + if (version_compare(WC_VERSION, '3.3', '>=')) {
75 + $wc_session->init();
76 + }
71 77
72 - if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
73 - define( 'WOOCOMMERCE_CHECKOUT', true );
74 - }
78 + $wc_session->set_customer_session_cookie(true);
79 + }
75 80
76 - WC()->checkout()->process_checkout();
81 + /**
82 + * Create the woocommerce order in the BO
83 + */
84 + public function ajax_create_order(): void
85 + {
86 + if (WC()->cart->is_empty()) {
87 + wp_send_json_error(__('Empty cart', 'payplug'));
88 + }
77 89
78 - die( 0 );
79 - }
90 + if (!defined('WOOCOMMERCE_CHECKOUT')) {
91 + define('WOOCOMMERCE_CHECKOUT', true);
92 + }
80 93
81 - /**
82 - * Update Payplug API Payment for Apple Pay
83 - */
84 - public function applepay_update_payment() {
94 + WC()->checkout()->process_checkout();
85 95
86 - $options = get_option('woocommerce_payplug_settings', []);
87 - $order_id = $_POST['order_id'];
88 - $payment_id = $_POST['payment_id'];
96 + die(0);
97 + }
89 98
90 - try{
91 - \Payplug\Payplug::init(array(
92 - 'secretKey' => @$options['payplug_live_key'],
93 - 'apiVersion' => "2019-08-06",
94 - ));
99 + /**
100 + * Process Apple Pay payment for an existing order on the order-pay page.
101 + */
102 + public function ajax_apple_pay_create_order_pay(): void
103 + {
104 + if (!check_ajax_referer('woocommerce-process_checkout', 'woocommerce-process-checkout-nonce', false)) {
105 + wp_send_json([
106 + 'result' => 'failure',
107 + 'messages' => '<ul class="woocommerce-error"><li>' . __('Invalid order.', 'payplug') . '</li></ul>',
108 + ]);
95 109
96 - $apple_pay = array();
97 - $apple_pay['payment_token'] = $_POST['payment_token'];
98 - $payment = \Payplug\Payment::retrieve($payment_id);
110 + return;
111 + }
99 112
100 - $data = array( 'apple_pay' => $apple_pay );
101 - $update = $payment->update($data);
113 + $order_id = isset($_POST['order_id']) ? absint(wp_unslash($_POST['order_id'])) : 0;
114 + $order_key = isset($_POST['order_key']) ? wc_clean(wp_unslash($_POST['order_key'])) : '';
102 115
103 - wp_send_json_success([ "result" => $update->is_paid ]);
116 + $order = $order_id ? wc_get_order($order_id) : null;
117 + if (!$order || !hash_equals($order->get_order_key(), $order_key)) {
118 + wp_send_json([
119 + 'result' => 'failure',
120 + 'messages' => '<ul class="woocommerce-error"><li>' . __('Invalid order.', 'payplug') . '</li></ul>',
121 + ]);
104 122
105 - }catch (\Exception $e){
106 - wp_send_json_error($e->getMessage());
107 - }
108 - }
123 + return;
124 + }
109 125
126 + // This AJAX request's own URL never carries the order-pay query var (only the page
127 + // that triggered it does), but WC_Payment_Gateway::get_order_total() - used by this
128 + // plugin's own check_gateway() filter on woocommerce_available_payment_gateways to
129 + // enforce per-method amount permissions - reads that query var to know whether to use
130 + // the order's total or the (here empty, on order-pay) cart's. Left unset, it falls
131 + // back to a cart total of 0, which the amount-permission check then rejects, making
132 + // Apple Pay appear unavailable below. Setting it restores the normal, fully validated
133 + // availability check (API key, requirements, amount permissions, etc.).
134 + global $wp_query;
135 + $wp_query->set('order-pay', $order_id);
136 +
137 + $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
138 + if (!isset($available_gateways['apple_pay'])) {
139 + wp_send_json([
140 + 'result' => 'failure',
141 + 'messages' => '<ul class="woocommerce-error"><li>' . __('Apple Pay not available.', 'payplug') . '</li></ul>',
142 + ]);
143 +
144 + return;
145 + }
146 +
147 + try {
148 + $result = $available_gateways['apple_pay']->process_payment($order_id);
149 + wp_send_json($result);
150 + } catch (\Exception $e) {
151 + wp_send_json([
152 + 'result' => 'failure',
153 + 'messages' => '<ul class="woocommerce-error"><li>' . esc_html($e->getMessage()) . '</li></ul>',
154 + ]);
155 + }
156 + }
157 +
158 + /**
159 + * Create the woocommerce order in the BO
160 + */
161 + public function ajax_create_payment(): void
162 + {
163 + global $wp;
164 +
165 + $https_referer = wc_clean(wp_unslash($_POST['_wp_http_referer'] ?? ''));
166 + $path = wp_parse_url($https_referer) ?: [];
167 + $output = [];
168 + if (!empty($path['query'])) {
169 + wp_parse_str($path['query'], $output);
170 + }
171 +
172 + if (isset($output['order-pay'])) {
173 + $order_id = absint($output['order-pay']);
174 + } else {
175 + preg_match('/(?<=order-pay\/)\d*/', $path['path'] ?? '', $matches);
176 + $order_id = !empty($matches[0]) ? absint($matches[0]) : 0;
177 + }
178 +
179 + // The referer is client-supplied and can be spoofed: only trust it as an order-pay
180 + // request once the order it names is confirmed real and the key matches, exactly
181 + // like the order-pay AJAX flows below already require (create_payment_intent,
182 + // ajax_apple_pay_create_order_pay). Otherwise fall through as a regular checkout.
183 + $order = $order_id ? wc_get_order($order_id) : false;
184 + if (!$order instanceof \WC_Order || !hash_equals($order->get_order_key(), wc_clean(wp_unslash($output['key'] ?? '')))) {
185 + $order_id = 0;
186 + }
187 +
188 + // Order-pay repays an existing order, whose line items live on the order itself,
189 + // not the session cart - which is legitimately empty here (the customer already
190 + // completed checkout for it), so only require a non-empty cart on a fresh checkout.
191 + if (empty($order_id) && WC()->cart->is_empty()) {
192 + wp_send_json_error(__('Empty cart', 'payplug'));
193 + }
194 +
195 + if (!defined('WOOCOMMERCE_CHECKOUT')) {
196 + define('WOOCOMMERCE_CHECKOUT', true);
197 + }
198 +
199 + $payment_method = $_POST['payment_method'];
200 +
201 + if ($payment_method === 'payplug' || $payment_method === 'american_express') {
202 + $method = $this->get_configuration()->get_option('payment_methods.configuration.payplug.embedded_mode');
203 + if ('integrated' != $method && 'popup' != $method) {
204 + $this->ajax_create_order();
205 + }
206 + } else {
207 + $this->ajax_create_order();
208 + }
209 +
210 + $this->process_order_payment($order_id, $payment_method);
211 + }
212 +
213 + /**
214 + * wordpress class-wc-checkout.php
215 + * We don't need all the other verifications, at this point customer already had the checkout and it's all valid, the order already exists
216 + * We can+t use WP method because it's protected
217 + * Process an order that does require payment.
218 + *
219 + * @since 3.0.0
220 + *
221 + * @param int $order_id Order ID.
222 + * @param string $payment_method Payment method.
223 + */
224 + protected function process_order_payment($order_id, $payment_method): void
225 + {
226 + // This AJAX request's own URL never carries the order-pay query var (only the page
227 + // that triggered it does), but WC_Payment_Gateway::get_order_total() - used by this
228 + // plugin's own check_gateway() filter on woocommerce_available_payment_gateways to
229 + // enforce per-method amount permissions - reads that query var to know whether to use
230 + // the order's total or the (here empty, on order-pay) cart's. Left unset, it falls
231 + // back to a cart total of 0, which the amount-permission check then rejects, making
232 + // every gateway appear unavailable below. Setting it restores the normal, fully
233 + // validated availability check (API key, requirements, amount permissions, etc.).
234 + global $wp_query;
235 + $wp_query->set('order-pay', $order_id);
236 +
237 + $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
238 +
239 + if (!isset($available_gateways[$payment_method])) {
240 + return;
241 + }
242 +
243 + // Store Order ID in session so it can be re-used after payment failure.
244 + WC()->session->set('order_awaiting_payment', $order_id);
245 +
246 + // Process Payment.
247 + $result = $available_gateways[$payment_method]->process_payment($order_id);
248 +
249 + // Redirect to success/confirmation/payment page.
250 + if (isset($result['result']) && 'success' === $result['result']) {
251 + $result['order_id'] = $order_id;
252 +
253 + $result = apply_filters('woocommerce_payment_successful_result', $result, $order_id);
254 +
255 + if (!wp_doing_ajax()) {
256 + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
257 + wp_redirect($result['redirect']);
258 + exit;
259 + }
260 +
261 + wp_send_json($result);
262 + }
263 + }
264 +
265 + /**
266 + * Update Payplug API Payment for Apple Pay
267 + */
268 + public function applepay_update_payment(): void
269 + {
270 + $payment_id = $_POST['payment_id'];
271 + $apiService = new \Payplug\PayplugWoocommerce\Service\Api();
272 + $mode = PayplugWoocommerceHelper::check_mode() ? 'live' : 'test';
273 + $bearer_token = $apiService->get_bearer_token($mode);
274 + try {
275 + \Payplug\Payplug::init([
276 + 'secretKey' => $bearer_token,
277 + 'apiVersion' => '2019-08-06',
278 + ]);
279 + } catch (\Exception $e) {
280 + $is_401 = (method_exists($e, 'getCode') && $e->getCode() == 401)
281 + || strpos($e->getMessage(), '401') !== false;
282 + if ($is_401 && $this->try_refresh_jwt($apiService, $mode)) {
283 + try {
284 + \Payplug\Payplug::init([
285 + 'secretKey' => $apiService->get_bearer_token($mode),
286 + 'apiVersion' => '2019-08-06',
287 + ]);
288 + } catch (\Exception $e) {
289 + PayplugWoocommerceHelper::payplug_logout();
290 + wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));
291 +
292 + return;
293 + }
294 + } else {
295 + if ($is_401) {
296 + PayplugWoocommerceHelper::payplug_logout();
297 + wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));
298 + } else {
299 + wp_send_json_error($e->getMessage());
300 + }
301 +
302 + return;
303 + }
304 + }
305 + $apple_pay = [];
306 + $apple_pay['payment_token'] = $_POST['payment_token'];
307 + $payment = \Payplug\Payment::retrieve($payment_id);
308 + $data = ['apple_pay' => $apple_pay];
309 + $update = $payment->update($data);
310 + wp_send_json_success(['result' => $update->is_paid]);
311 + }
312 +
313 + public function applepay_get_order_totals(): void
314 + {
315 + try {
316 + wp_send_json_success(WC()->cart->total);
317 + } catch (\Exception $e) {
318 + PayplugGateway::log($e->getMessage());
319 + wp_send_json_error($e->getMessage());
320 + }
321 + }
322 +
323 + /**
324 + * Empty cart for Apple Pay on product page
325 + */
326 + public function applepay_empty_cart(): void
327 + {
328 + try {
329 + WC()->cart->empty_cart();
330 + wp_send_json_success();
331 + } catch (\Exception $e) {
332 + wp_send_json_error([
333 + 'message' => __('Your order was cancelled.', 'woocommerce'),
334 + ]);
335 + }
336 + }
337 +
338 + /**
339 + * Add the product on the current page to the cart for Apple Pay on product page
340 + */
341 + public function applepay_add_to_cart(): void
342 + {
343 + try {
344 + if (!empty($_POST['product_id'])) {
345 + $product_id = $_POST['product_id'];
346 + } else {
347 + $product_id = $_POST['product_variation_id'];
348 + }
349 +
350 + $product_quantity = !empty($_POST['product_quantity']) ? $_POST['product_quantity'] : 1;
351 +
352 + WC()->cart->add_to_cart($product_id, $product_quantity);
353 + wp_send_json_success([
354 + 'total' => WC()->cart->total - WC()->cart->shipping_total,
355 + ]);
356 + } catch (\Exception $e) {
357 + wp_send_json_error([
358 + 'message' => __('Your order was cancelled.', 'woocommerce'),
359 + ]);
360 + }
361 + }
362 +
363 + /**
364 + * Limit string length.
365 + *
366 + * @param string $value
367 + * @param int $maxlength
368 + *
369 + * @return string
370 + */
371 + public function limit_length($value, $maxlength = 100)
372 + {
373 + return (strlen($value) > $maxlength) ? substr($value, 0, $maxlength) : $value;
374 + }
375 +
376 + public function check_payment(): void
377 + {
378 + global $wpdb;
379 + $payment_id = $_POST['payment_id'];
380 + if (empty($payment_id)) {
381 + wp_send_json_error(__('Invalid request.', 'payplug'));
382 + }
383 + $apiService = new \Payplug\PayplugWoocommerce\Service\Api();
384 + $mode = PayplugWoocommerceHelper::check_mode() ? 'live' : 'test';
385 + $bearer_token = $apiService->get_bearer_token($mode);
386 + try {
387 + \Payplug\Payplug::init([
388 + 'secretKey' => $bearer_token,
389 + 'apiVersion' => '2019-08-06',
390 + ]);
391 + $payment = \Payplug\Payment::retrieve($payment_id);
392 + } catch (\Exception $e) {
393 + $is_401 = (method_exists($e, 'getCode') && $e->getCode() == 401)
394 + || strpos($e->getMessage(), '401') !== false;
395 + if ($is_401) {
396 + if ($this->try_refresh_jwt($apiService, $mode)) {
397 + try {
398 + \Payplug\Payplug::init([
399 + 'secretKey' => $apiService->get_bearer_token($mode),
400 + 'apiVersion' => '2019-08-06',
401 + ]);
402 + $payment = \Payplug\Payment::retrieve($payment_id);
403 + } catch (\Exception $e) {
404 + PayplugWoocommerceHelper::payplug_logout();
405 + wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));
406 +
407 + return;
408 + }
409 + } else {
410 + PayplugWoocommerceHelper::payplug_logout();
411 + wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));
412 +
413 + return;
414 + }
415 + } else {
416 + $order_id = $this->getOrderFromPaymentId($payment_id);
417 + $order = wc_get_order($order_id);
418 + PayplugGateway::log(
419 + sprintf(
420 + 'Order #%s : An error occurred while retrieving the payment data with the message : %s',
421 + $order_id,
422 + $e->getMessage()
423 + )
424 + );
425 + wp_send_json_error($e->getMessage());
426 + }
427 + }
428 + $order_id = $this->getOrderFromPaymentId($payment_id);
429 + $order = wc_get_order($order_id);
430 + $return_url = esc_url_raw($order->get_checkout_order_received_url());
431 + if ((isset($payment->failure)) && (!empty($payment->failure)) || ($payment->is_paid === false && is_null($payment->paid_at))) {
432 + $order->update_status('failed', __('Order cancelled by customer.', 'woocommerce'));
433 + wp_send_json_error(
434 + [
435 + 'code' => isset($payment->failure->code) ? $payment->failure->code : 500,
436 + 'message' => !empty($payment->failure->message) ? $payment->failure->message : __('payplug_integrated_payment_error', 'payplug'),
437 + 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
438 + ]
439 + );
440 + }
441 +
442 + wp_send_json_success([
443 + 'payment_id' => $payment->id,
444 + 'result' => 'success',
445 + 'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
446 + 'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null,
447 + ]);
448 + }
449 +
450 + /**
451 + * Attempt to regenerate the JWT for the given mode and persist the new token.
452 + *
453 + * @param \Payplug\PayplugWoocommerce\Service\Api $apiService
454 + * @param string $mode
455 + *
456 + * @return bool True if a new JWT was successfully obtained and stored.
457 + */
458 + private function try_refresh_jwt($apiService, $mode)
459 + {
460 + $configuration = $this->get_configuration();
461 + $options = $configuration->get_options();
462 + $oauth_client_data = isset($options['oauth_client_data']) ? json_decode($options['oauth_client_data'], true) : [];
463 + $client_data = isset($oauth_client_data[$mode]) ? $oauth_client_data[$mode] : [];
464 + if (empty($client_data)) {
465 + return false;
466 + }
467 + $new_jwt = $apiService->generate_jwt(
468 + isset($client_data['client_id']) ? $client_data['client_id'] : '',
469 + isset($client_data['client_secret']) ? $client_data['client_secret'] : ''
470 + );
471 + if (empty($new_jwt) || !isset($new_jwt['access_token'])) {
472 + return false;
473 + }
474 + $jwt = isset($options['jwt']) ? json_decode($options['jwt'], true) : [];
475 + $jwt[$mode] = $new_jwt;
476 + $api_keys = isset($options['api_key']) ? json_decode($options['api_key'], true) : [];
477 + $api_keys[$mode] = $new_jwt['access_token'];
478 + $configuration->update_option('jwt', json_encode($jwt));
479 + $configuration->update_option('api_key', json_encode($api_keys));
480 +
481 + return true;
482 + }
483 +
484 + /**
485 + * @param $payment_id
486 + *
487 + * @return int|string|null
488 + */
489 + private function getOrderFromPaymentId($payment_id)
490 + {
491 + global $wpdb;
492 +
493 + if (OrderUtil::custom_orders_table_usage_is_enabled()) {
494 + $orders = wc_get_orders(
495 + [
496 + 'field_query' => [
497 + [
498 + 'key' => 'transaction_id',
499 + 'comparison' => $payment_id,
500 + ],
501 + ],
502 + ]
503 + );
504 + $order = $orders[0];
505 + $order_id = $order->get_id();
506 + } else {
507 + $sql = 'SELECT post_id
508 + FROM $wpdb->postmeta
509 + WHERE meta_key = "_transaction_id" AND meta_value = %s';
510 + $order_id = $wpdb->get_var(
511 + $wpdb->prepare(
512 + $sql,
513 + $payment_id
514 + )
515 + );
516 + }
517 +
518 + return $order_id;
519 + }
520 +
521 + public function create_payment_intent(): void
522 + {
523 + if (!check_ajax_referer('woocommerce-process_checkout', 'woocommerce-process-checkout-nonce', false)) {
524 + wp_send_json_error(__('Invalid order.', 'payplug'), 403);
525 +
526 + return;
527 + }
528 +
529 + $order_id = isset($_POST['order_id']) ? absint(wp_unslash($_POST['order_id'])) : 0;
530 + $this->gateway = $this->get_payplug_gateway(isset($_POST['gateway']) ? wc_clean(wp_unslash($_POST['gateway'])) : '');
531 + $order = wc_get_order($order_id);
532 +
533 + if (!$order instanceof \WC_Order || !$this->gateway) {
534 + wp_send_json_error(__('Invalid order.', 'payplug'));
535 +
536 + return;
537 + }
538 +
539 + // On order-pay, closing the payment sheet should keep the customer on the order-pay
540 + // page, not cancel the order and send them to the cart like a fresh checkout attempt
541 + // would.
542 + $is_order_pay = is_wc_endpoint_url('order-pay') || !empty($_POST['order_pay_key']);
543 +
544 + if (!empty($_POST['order_pay_key'])) {
545 + $order_pay_key = wc_clean(wp_unslash($_POST['order_pay_key']));
546 + if (!hash_equals($order->get_order_key(), $order_pay_key)) {
547 + wp_send_json_error(__('Invalid order.', 'payplug'));
548 +
549 + return;
550 + }
551 + }
552 +
553 + $cancel_url = $is_order_pay
554 + ? esc_url_raw($order->get_checkout_payment_url())
555 + : esc_url_raw($order->get_cancel_order_url_raw());
556 +
557 + $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
558 + $return_url = esc_url_raw($order->get_checkout_order_received_url());
559 + $address_data = PayplugAddressData::from_order($order);
560 + $amount = (int) PayplugWoocommerceHelper::get_payplug_amount($order->get_total());
561 + $amount = $this->gateway->validate_order_amount($amount);
562 +
563 + $payment_data = [
564 + 'amount' => $amount,
565 + 'currency' => get_woocommerce_currency(),
566 + 'allow_save_card' => $this->gateway->save_card_available() && (int) $customer_id > 0,
567 + 'billing' => $address_data->get_billing(),
568 + 'shipping' => $address_data->get_shipping(),
569 + 'hosted_payment' => [
570 + 'return_url' => $return_url,
571 + ],
572 + 'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
573 + 'metadata' => [
574 + 'order_id' => $order_id,
575 + 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
576 + 'domain' => $this->limit_length(esc_url_raw(home_url()), 500),
577 + ],
578 + ];
579 +
580 + if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) {
581 + $payment_data['metadata']['woocommerce_block'] = 'CHECKOUT';
582 + } elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) {
583 + $payment_data['metadata']['woocommerce_block'] = 'CART';
584 + }
585 +
586 + if ($this->gateway->id === 'apple_pay') {
587 + unset($payment_data['allow_save_card']);
588 +
589 + $payment_data['payment_method'] = $this->gateway->id;
590 + $payment_data['payment_context'] = [
591 + 'apple_pay' => [
592 + 'domain_name' => $this->gateway->domain_name,
593 + 'application_data' => base64_encode(json_encode([
594 + 'apple_pay_domain' => $this->gateway->domain_name,
595 + ])),
596 + ],
597 + ];
598 + $payment_data['hosted_payment']['cancel_url'] = $cancel_url;
599 + $payment_data['metadata']['applepay_workflow'] = 'checkout';
600 + }
601 +
602 + $method = $this->get_configuration()->get_option('payment_methods.configuration.payplug.embedded_mode');
603 +
604 + if ('integrated' == $method && $this->gateway->id === 'payplug') {
605 + $payment_data['initiator'] = 'PAYER';
606 + $payment_data['integration'] = 'INTEGRATED_PAYMENT';
607 + unset($payment_data['hosted_payment']['cancel_url']);
608 + }
609 +
610 + if ('popup' == $method && $this->gateway->id === 'american_express') {
611 + $payment_data['payment_method'] = $this->gateway->id;
612 + }
613 +
614 + /**
615 + * Filter the payment data before it's used
616 + *
617 + * @param array $payment_data
618 + * @param int $order_id
619 + * @param array $customer_details
620 + * @param PayplugAddressData $address_data
621 + */
622 + $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);
623 +
624 + try {
625 + $payment = $this->gateway->payplug_api->payment_create($payment_data);
626 + } catch (HttpException $e) {
627 + PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
628 + wp_send_json_error(__('Payment processing failed. Please retry.', 'payplug'));
629 +
630 + return;
631 + } catch (\Exception $e) {
632 + PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
633 + wp_send_json_error(__('Payment processing failed. Please retry.', 'payplug'));
634 +
635 + return;
636 + }
637 +
638 + // Save transaction id on the order
639 + PayplugWoocommerceHelper::is_pre_30() ? update_post_meta($order_id, '_transaction_id', $payment->id) : $order->set_transaction_id($payment->id);
640 +
641 + if (is_callable([$order, 'save'])) {
642 + $order->save();
643 + }
644 +
645 + $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
646 + PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
647 +
648 + wp_send_json_success([
649 + 'payment_id' => $payment->id,
650 + 'merchant_session' => isset($payment->payment_method['merchant_session']) ? $payment->payment_method['merchant_session'] : null,
651 + 'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
652 + 'cancel' => $cancel_url,
653 + ]);
654 + }
655 +
656 + /**
657 + * Returns an instantiated gateway.
658 + *
659 + * @return PayplugGateway
660 + */
661 + protected function get_payplug_gateway($id)
662 + {
663 + if (!isset($this->gateway)) {
664 + $gateways = WC()->payment_gateways()->payment_gateways();
665 + foreach ($gateways as $gateway) {
666 + if ($gateway->id === $id) {
667 + return $gateway;
668 + }
669 + }
670 + }
671 + }
110 672 }