PluginProbe
PayPlug for WooCommerce (Official) / 2.18.0
PayPlug for WooCommerce (Official) v2.18.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 / ApplePay.php

ApplePay.php in PayPlug for WooCommerce (Official) 2.18.0, at src/Controller/ApplePay.php

679 lines 23.3 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 function is_cart;
6 use function is_product;
7 use Payplug\Exception\HttpException;
8 use Payplug\PayplugWoocommerce\Gateway\PayplugAddressData;
9 use Payplug\PayplugWoocommerce\Gateway\PayplugGateway;
10 use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper;
11 use Payplug\Resource\Payment as PaymentResource;
12
13 /**
14 * ApplePay controller for handling Apple Pay payment logic in WooCommerce.
15 */
16 class ApplePay extends PayplugGateway
17 {
18 public $domain_name = '';
19
20 protected $cart = false;
21
22 protected $checkout = false;
23
24 protected $carriers = [];
25
26 const ENABLE_ON_TEST_MODE = false;
27
28 public $image = 'apple-pay-checkout.svg';
29
30 protected $product = false;
31
32 public function __construct()
33 {
34 parent::__construct();
35
36 /** @var \WC_Settings_API override $id */
37 $this->id = 'apple_pay';
38
39 /** @var \WC_Payment_Gateway overwrite for apple pay settings */
40 $this->method_title = __('payplug_apple_pay_title', 'payplug');
41 $this->method_description = '';
42 $this->has_fields = false;
43
44 $this->title = __('payplug_apple_pay_title', 'payplug');
45 $this->description = '<div id="apple-pay-button-wrapper"><apple-pay-button buttonstyle="black" type="pay" locale="' . get_locale() . '"></apple-pay-button></div>';
46 $this->domain_name = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : parse_url(home_url(), PHP_URL_HOST);
47 $this->enabled = 'no';
48
49 if ($this->checkApplePay() && is_admin()) {
50 $this->enabled = 'yes';
51 } elseif ($this->checkApplePay() && $this->isSSL()) {
52 if (!is_admin() && $this->get_button_checkout()) {
53 $this->enabled = 'yes';
54 }
55
56 if (!is_admin()) {
57 if (!PayplugWoocommerceHelper::is_checkout_block() && $this->get_button_checkout()) {
58 $this->add_apple_pay_css();
59 add_action('wp_enqueue_scripts', [$this, 'add_apple_pay_js']);
60 }
61
62 if ($this->get_button_cart() && !PayplugWoocommerceHelper::is_cart_block() && !PayplugWoocommerceHelper::is_subscription()) {
63 $this->enabled = 'yes';
64 $this->add_apple_pay_css();
65 add_action('woocommerce_proceed_to_checkout', [$this, 'add_apple_pay_cart_js'], 15);
66 }
67
68 if ($this->get_button_product() && !PayplugWoocommerceHelper::is_product_block()) {
69 $this->enabled = 'yes';
70 $this->add_apple_pay_css();
71 add_action('woocommerce_after_add_to_cart_button', [$this, 'add_apple_pay_product_js'], 15);
72 }
73 }
74 }
75 }
76
77 /**
78 * Processes admin options for Apple Pay settings.
79 *
80 * @return bool|void
81 */
82 public function process_admin_options()
83 {
84 $data = $this->get_post_data();
85 if (isset($data['woocommerce_payplug_mode'])) {
86 if ($this->get_post_data()['woocommerce_payplug_mode'] === '0') {
87 $options = $this->get_configuration()->get_options();
88 $options['payment_methods']['configuration']['apple_pay']['active'] = false;
89 $this->get_configuration()->update_options($options);
90 }
91 }
92 if (isset($data['woocommerce_payplug_apple_pay'])) {
93 if (($data['woocommerce_payplug_apple_pay'] == 1) && (!$this->checkApplePay())) {
94 add_action('admin_notices', [$this, 'display_notice']);
95 }
96 }
97 }
98
99 /**
100 * Checks if Apple Pay is authorized and available.
101 *
102 * @return bool
103 */
104 public function checkApplePay()
105 {
106 $options = $this->settings;
107
108 //check if module is enabled
109 if (!isset($options['enabled']) || !$options['enabled']) {
110 return false;
111 }
112
113 if (!isset($options['payment_methods']) || empty($options['payment_methods'])) {
114 return false;
115 }
116
117 //it's disabled
118 if (!(bool) $options['payment_methods']['configuration']['apple_pay']['active']) {
119 return false;
120 }
121
122 //Amount validations
123 if (is_cart() && !empty(WC()->cart)) {
124 $order_amount = (float) WC()->cart->total;
125 if ($order_amount < self::MIN_AMOUNT || $order_amount > self::MAX_AMOUNT) {
126 return false;
127 }
128 }
129
130 $display = json_decode($options['payment_methods']['configuration']['apple_pay']['display'], true);
131 $this->set_button_checkout($display['checkout']);
132 $this->set_button_cart($display['cart']);
133 $this->set_button_product($display['product']);
134
135 $carriers = json_decode($options['payment_methods']['configuration']['apple_pay']['carriers'], true);
136 if (!empty($carriers)) {
137 $this->set_carriers($carriers);
138 }
139
140 $account = PayplugWoocommerceHelper::generic_get_account_data_from_options($this->id);
141 //no auth
142 if (!isset($account['payment_methods']['apple_pay']) || !isset($account['payment_methods']['apple_pay']['allowed_domain_names'])) {
143 return false;
144 }
145
146 //$account has permissions to use apple_pay
147 $auth = isset($account['payment_methods']['apple_pay']['enabled']) && $account['payment_methods']['apple_pay']['enabled'];
148 $domain = parse_url(get_site_url());
149 $auth_domains = in_array($domain['host'], $account['payment_methods']['apple_pay']['allowed_domain_names']);
150
151 //lost auth
152 if (!($auth && $auth_domains)) {
153 return false;
154 }
155
156 return true;
157 }
158
159 /**
160 * Outputs the payment fields on the checkout page.
161 *
162 * @return void
163 */
164 public function payment_fields()
165 {
166 $description = $this->get_description();
167
168 if (!empty($description)) {
169 echo wpautop(wptexturize($description));
170 }
171 }
172
173 /**
174 * extend the woocommmerce get description to include personalized html
175 *
176 * @return mixed|string|null
177 */
178 public function get_description()
179 {
180 return apply_filters('woocommerce_gateway_description', $this->description, $this->id);
181 }
182
183 /**
184 * Enqueues Apple Pay scripts for the cart page.
185 *
186 * @return void
187 */
188 public function add_apple_pay_cart_js()
189 {
190 wp_enqueue_script('apple-pay-sdk', 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js', [], false, true);
191 wp_enqueue_script('payplug-apple-pay-cart', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/js/payplug-apple-pay-cart.js', ['jquery', 'apple-pay-sdk'], PAYPLUG_GATEWAY_VERSION, true);
192 wp_localize_script(
193 'payplug-apple-pay-cart',
194 'apple_pay_params',
195 [
196 'ajax_url_applepay_get_shippings' => \WC_AJAX::get_endpoint('applepay_get_shippings'),
197 'ajax_url_place_order_with_dummy_data' => \WC_AJAX::get_endpoint('place_order_with_dummy_data'),
198 'ajax_url_update_applepay_order' => \WC_AJAX::get_endpoint('update_applepay_order'),
199 'ajax_url_update_applepay_payment' => \WC_AJAX::get_endpoint('update_applepay_payment'),
200 'ajax_url_applepay_get_order_totals' => \WC_AJAX::get_endpoint('applepay_get_order_totals'),
201 'ajax_url_applepay_cancel_order' => \WC_AJAX::get_endpoint('applepay_cancel_order'),
202 'is_cart' => is_cart(),
203 'is_virtual' => !$this->is_shipping_required(),
204 'cart_shipping' => WC()->cart->get_shipping_total(),
205 'countryCode' => WC()->customer->get_billing_country(),
206 'currencyCode' => get_woocommerce_currency(),
207 'apple_pay_domain' => $this->domain_name,
208 ]
209 );
210
211 if ($this->checkButtonVisibility()) {
212 echo $this->get_description();
213 }
214 }
215
216 /**
217 * Enqueues Apple Pay scripts for the product page.
218 *
219 * @return void
220 */
221 public function add_apple_pay_product_js()
222 {
223 global $product;
224 // Only dispay ApplePay on product page for simple and variable products
225 if ($product->get_type() != 'simple' && $product->get_type() != 'variable') {
226 return;
227 }
228 $apple_pay_params = [
229 'ajax_url_applepay_get_shippings' => \WC_AJAX::get_endpoint('applepay_get_shippings'),
230 'ajax_url_place_order_with_dummy_data' => \WC_AJAX::get_endpoint('place_order_with_dummy_data'),
231 'ajax_url_update_applepay_order' => \WC_AJAX::get_endpoint('update_applepay_order'),
232 'ajax_url_update_applepay_payment' => \WC_AJAX::get_endpoint('update_applepay_payment'),
233 'ajax_url_applepay_get_order_totals' => \WC_AJAX::get_endpoint('applepay_get_order_totals'),
234 'ajax_url_applepay_cancel_order' => \WC_AJAX::get_endpoint('applepay_cancel_order'),
235 'ajax_url_applepay_empty_cart' => \WC_AJAX::get_endpoint('applepay_empty_cart'),
236 'ajax_url_applepay_add_to_cart' => \WC_AJAX::get_endpoint('applepay_add_to_cart'),
237 'is_product' => is_product(),
238 'is_virtual' => $product->is_virtual(),
239 'cart_shipping' => WC()->cart->get_shipping_total(),
240 'countryCode' => WC()->customer->get_billing_country(),
241 'currencyCode' => get_woocommerce_currency(),
242 'apple_pay_domain' => $_SERVER['HTTP_HOST'],
243 ];
244 wp_enqueue_script('apple-pay-sdk', 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js', [], false, true);
245 wp_enqueue_script('payplug-apple-pay-product', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/js/payplug-apple-pay-product.js', ['jquery', 'apple-pay-sdk'], PAYPLUG_GATEWAY_VERSION, true);
246 wp_localize_script('payplug-apple-pay-product', 'apple_pay_params', $apple_pay_params);
247
248 if ($this->checkButtonVisibility()) {
249 echo $this->get_description();
250 }
251 }
252
253 /**
254 * Check if the shipping address is a carrier
255 *
256 * @return bool
257 */
258 private function checkButtonVisibility()
259 {
260 $apple_carriers = $this->get_carriers();
261 $allowed = false;
262 $post = $this->get_post_data();
263 $chosen_method = isset($post['shipping_method'][0]) ? $post['shipping_method'][0] : null;
264
265 if (empty($chosen_method)) {
266 $chosen_method = !empty(WC()->session->chosen_shipping_methods[0]) ? WC()->session->chosen_shipping_methods[0] : null;
267 }
268
269 if (!$this->is_shipping_required() || is_product()) {
270 return true;
271 }
272
273 foreach (WC()->shipping()->get_packages() as $i => $package) {
274 $available_rates = !empty($package['rates']) ? $package['rates'] : [];
275 if (!empty($available_rates)) {
276 foreach ($available_rates as $method) {
277 if (in_array($method->get_method_id(), $apple_carriers)) {
278 if ($chosen_method === $method->get_method_id() . ':' . $method->get_instance_id()) {
279 $allowed = true;
280 }
281 }
282 }
283 }
284 }
285
286 return $allowed;
287 }
288
289 /**
290 * check if the shipping is required or not
291 *
292 * @return bool
293 */
294 public function is_shipping_required()
295 {
296 $cart = WC()->cart->get_cart();
297
298 $required = true;
299 foreach ($cart as $cart_item) {
300 if (!empty($cart_item['product_id'])) {
301 $product = wc_get_product($cart_item['product_id']);
302
303 //not required if it enters here
304 if (!$product->is_virtual() && !$product->is_downloadable()) {
305 return true;
306 }
307
308 $required = false;
309 }
310 }
311
312 return $required;
313 }
314
315 /**
316 * Display unauthorized error
317 *
318 * @return void
319 */
320 public static function display_notice()
321 {
322 ?>
323 <div class="notice notice-error is-dismissible">
324 <p><?php echo __('payplug_apple_pay_unauthorized_error', 'payplug'); ?></p>
325 </div>
326 <?php
327 }
328
329 /**
330 * Checks if the current connection is using SSL.
331 *
332 * @return bool
333 */
334 public function isSSL()
335 {
336 if (!empty($_SERVER['HTTPS'])) {
337 if ('on' == strtolower($_SERVER['HTTPS'])) {
338 return true;
339 }
340 } elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) {
341 return true;
342 }
343
344 if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') {
345 return true;
346 }
347
348 return false;
349 }
350
351 /**
352 * Enqueues Apple Pay CSS styles.
353 *
354 * @return void
355 */
356 public function add_apple_pay_css()
357 {
358 wp_enqueue_style('payplug-apple-pay', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/css/payplug-apple-pay.css', [], PAYPLUG_GATEWAY_VERSION);
359 }
360
361 /**
362 * Enqueues Apple Pay JavaScript for the checkout page.
363 *
364 * @return void
365 */
366 public function add_apple_pay_js()
367 {
368 wp_enqueue_script('apple-pay-sdk', 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js', [], false, true);
369 wp_enqueue_script(
370 'payplug-apple-pay',
371 PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/js/payplug-apple-pay.js',
372 [
373 'jquery',
374 'apple-pay-sdk',
375 ],
376 PAYPLUG_GATEWAY_VERSION,
377 true
378 );
379 wp_localize_script(
380 'payplug-apple-pay',
381 'apple_pay_params',
382 [
383 'ajax_url_payplug_create_order' => \WC_AJAX::get_endpoint('payplug_create_order'),
384 'ajax_url_applepay_update_payment' => \WC_AJAX::get_endpoint('applepay_update_payment'),
385 'ajax_url_applepay_get_order_totals' => \WC_AJAX::get_endpoint('applepay_get_order_totals'),
386 'countryCode' => WC()->customer->get_billing_country(),
387 'currencyCode' => get_woocommerce_currency(),
388 'total' => WC()->cart->total,
389 'is_checkout' => is_checkout(),
390 'apple_pay_domain' => $this->domain_name,
391 ]
392 );
393 }
394
395 /**
396 * Gets the Apple Pay payment icon HTML.
397 *
398 * @return string
399 */
400 public function get_icon()
401 {
402 $available_img = 'apple-pay-checkout.svg';
403 $icons = apply_filters('payplug_payment_icons', [
404 'payplug' => sprintf('<img src="%s" alt="Apple Pay" class="payplug-payment-icon" />', esc_url(PAYPLUG_GATEWAY_PLUGIN_URL . '/assets/images/checkout/' . $available_img)),
405 ]);
406 $icons_str = '';
407 foreach ($icons as $icon) {
408 $icons_str .= $icon;
409 }
410
411 return $icons_str;
412 }
413
414 /**
415 * Processes a payment if it was already generated by an intent.
416 *
417 * @param $order
418 *
419 * @throws \Exception
420 *
421 * @return array|null
422 */
423 private function process_standard_intent_payment($order)
424 {
425 if (!is_wc_endpoint_url('order-pay') &&
426 PayplugWoocommerceHelper::is_checkout_block() &&
427 !empty($order->get_transaction_id())) {
428 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
429
430 try {
431 $payment = $this->payplug_api->payment_retrieve($order->get_transaction_id());
432 if (ob_get_length() > 0) {
433 ob_clean();
434 }
435
436 $return_url = esc_url_raw($order->get_checkout_order_received_url());
437
438 wp_send_json_success(
439 [
440 'payment_id' => $payment->id,
441 'result' => 'success',
442 'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
443 'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null,
444 ]
445 );
446
447 return ['stt' => 'OK'];
448 } catch (HttpException $e) {
449 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
450 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
451 } catch (\Exception $e) {
452 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
453 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
454 }
455 }
456
457 return null;
458 }
459
460 /**
461 * Processes a standard Apple Pay payment.
462 *
463 * @param \WC_Order $order
464 * @param int $amount
465 * @param int $customer_id
466 * @param string $workflow
467 *
468 * @throws \Exception
469 *
470 * @return array
471 */
472 public function process_standard_payment($order, $amount, $customer_id, $workflow = 'checkout')
473 {
474 $intent = $this->process_standard_intent_payment($order);
475 if (!empty($intent)) {
476 return $intent;
477 }
478
479 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
480 try {
481 $address_data = PayplugAddressData::from_order($order);
482
483 $return_url = esc_url_raw($order->get_checkout_order_received_url());
484
485 if (!(substr($return_url, 0, 4) === 'http')) {
486 $return_url = get_site_url() . $return_url;
487 }
488
489 // delivery_type must be removed in Apple Pay
490 $billing = $address_data->get_billing();
491 unset($billing['delivery_type']);
492 $shipping = $address_data->get_shipping();
493 unset($shipping['delivery_type']);
494
495 $payment_data = [
496 'amount' => $amount,
497 'currency' => get_woocommerce_currency(),
498 'payment_method' => $this->id,
499 'payment_context' => [
500 'apple_pay' => [
501 'domain_name' => $this->domain_name,
502 'application_data' => base64_encode(json_encode([
503 'apple_pay_domain' => $this->domain_name,
504 ])),
505 ],
506 ],
507 'billing' => $billing,
508 'shipping' => $shipping,
509 'hosted_payment' => [
510 'return_url' => $return_url,
511 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
512 ],
513 'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
514 'metadata' => [
515 'order_id' => $order_id,
516 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
517 'domain' => $this->domain_name,
518 'applepay_workflow' => $workflow,
519 ],
520 ];
521
522 if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) {
523 $payment_data['metadata']['woocommerce_block'] = 'CHECKOUT';
524 } elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) {
525 $payment_data['metadata']['woocommerce_block'] = 'CART';
526 }
527
528 /**
529 * Filter the payment data before it's used
530 *
531 * @param array $payment_data
532 * @param int $order_id
533 * @param array $customer_details
534 * @param PayplugAddressData $address_data
535 */
536 $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);
537 $payment = $this->payplug_api->payment_create($payment_data);
538
539 // Save transaction id for the order
540 PayplugWoocommerceHelper::is_pre_30()
541 ? update_post_meta($order_id, '_transaction_id', $payment->id)
542 : $order->set_transaction_id($payment->id);
543
544 if (is_callable([$order, 'save'])) {
545 $order->save();
546 }
547
548 /**
549 * Fires once a payment has been created.
550 *
551 * @param int $order_id Order ID
552 * @param PaymentResource $payment Payment resource
553 */
554 \do_action('payplug_gateway_payment_created', $order_id, $payment);
555
556 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
557 PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
558
559 PayplugGateway::log(sprintf('Payment creation complete for order #%s', $order_id));
560
561 return [
562 'result' => 'success',
563 'merchant_session' => $payment->payment_method['merchant_session'],
564 'payment_id' => $payment->id,
565 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
566 'return_url' => $return_url,
567 ];
568 } catch (HttpException $e) {
569 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
570 if ($workflow === 'cart') {
571 wp_send_json_error([
572 'code' => $e->getCode(),
573 'msg' => __('Payment processing failed. Please retry.', 'payplug'),
574 'order_id' => $order_id,
575 ]);
576 }
577 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
578 } catch (\Exception $e) {
579 PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
580 if ($workflow === 'cart') {
581 wp_send_json_error([
582 'code' => $e->getCode(),
583 'msg' => __('Payment processing failed. Please retry.', 'payplug'),
584 'order_id' => $order_id,
585 ]);
586 }
587 throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
588 }
589 }
590
591 /**
592 * Sets the checkout button status.
593 *
594 * @param bool $status
595 *
596 * @return void
597 */
598 private function set_button_checkout($status)
599 {
600 $this->checkout = $status;
601 }
602
603 /**
604 * Sets the cart button status.
605 *
606 * @param bool $status
607 *
608 * @return void
609 */
610 private function set_button_cart($status)
611 {
612 $this->cart = $status;
613 }
614
615 /**
616 * Sets the product button status.
617 *
618 * @param bool $status
619 *
620 * @return void
621 */
622 private function set_button_product($status)
623 {
624 $this->product = $status;
625 }
626
627 /**
628 * Gets the checkout button status.
629 *
630 * @return bool
631 */
632 private function get_button_checkout()
633 {
634 return $this->checkout;
635 }
636
637 /**
638 * Gets the cart button status.
639 *
640 * @return bool
641 */
642 public function get_button_cart()
643 {
644 return $this->cart;
645 }
646
647 /**
648 * Gets the product button status.
649 *
650 * @return bool
651 */
652 public function get_button_product()
653 {
654 return $this->product;
655 }
656
657 /**
658 * Gets the list of allowed carriers for Apple Pay.
659 *
660 * @return array
661 */
662 public function get_carriers()
663 {
664 return $this->carriers;
665 }
666
667 /**
668 * Sets the list of allowed carriers for Apple Pay.
669 *
670 * @param array $carriers
671 *
672 * @return void
673 */
674 private function set_carriers($carriers)
675 {
676 $this->carriers = $carriers;
677 }
678 }
679