PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.2.5
Pay with Vipps and MobilePay for WooCommerce v6.2.5
6.2.5 6.2.4 6.2.3 6.2.2 6.2.1 6.2.0 6.1.10 6.1.9 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1.0 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 6.0.0 5.4.3 5.4.2 All 187 releases
← All changes | payment/Vipps.class.php +102 -31 6.2.1 → 6.2.5 View file →
@@ -310,12 +310,9 @@
310 310
311 311
312 312 // We want this special page to have a certain title and maybe special scripts and so on,
313 313 // this gets run in template redirect for these pages.
314 - add_action('woo_vipps_before_handling_special_page', function ($action) {
315 - // Change title dynamically depending on action. LP 2026-09-02
316 - add_filter('the_title', [$this, 'vipps_special_page_endpoint_title'], 10, 2);
317 - });
314 + add_action('woo_vipps_before_handling_special_page', array($this, 'pre_special_page_actions'));
318 315
319 316 // Add an admin interface for this page as well IOK 2026-09-11
320 317 add_action('woocommerce_settings_pages', array($this, 'woocommerce_settings_pages'));
321 318
@@ -1046,12 +1043,30 @@
1046 1043
1047 1044 public function get_html_button_attrs_for_context($context = 'global') {
1048 1045 $options = get_option('vipps_button_options2', []);
1049 1046 if (!is_string($context)) $context = 'global';
1047 +
1048 + // Gutenberg express checkout buttons really want to be stretched, so we'll treat them somewhat differently.
1049 + $gutenberg = false;
1050 + if ($context == 'checkout_gutenberg') {
1051 + $context = 'checkout';
1052 + $gutenberg = true;
1053 + }
1054 + if ($context == 'cart_gutenberg') {
1055 + $context = 'cart';
1056 + $gutenberg = true;
1057 + }
1058 +
1050 1059 $config = $options['express']['configs'][$context] ?? [];
1051 - if (!$config || ($config['use-global-config'] ?? false)) {
1060 + $use_global = !$config || ($config['use-global-config'] ?? false);
1061 + if ($use_global) {
1052 1062 $config = $options['express']['configs']['global'] ?? $this->get_html_button_default_attrs();
1053 1063 }
1064 +
1065 + // see above.
1066 + if ($gutenberg) {
1067 + $config['stretched']='true';
1068 + }
1054 1069 return $config;
1055 1070 }
1056 1071
1057 1072 public function get_html_button_for_context($context = 'global') {
@@ -1138,8 +1153,10 @@
1138 1153 private function button_menu_express_section() {
1139 1154 $options = get_option('vipps_button_options2', []);
1140 1155 $express = $options['express'] ?? [];
1141 1156 $configs = $express['configs'] ?? [];
1157 +
1158 +
1142 1159 $contexts = [
1143 1160 'global' => __('Global', 'woo-vipps'),
1144 1161 'product' => __('Product', 'woo-vipps'),
1145 1162 'catalog' => __('Catalog', 'woo-vipps'),
@@ -1312,8 +1329,9 @@
1312 1329
1313 1330 // Swap to new context: set all input fields to the stored values if exists. LP 2026-06-25
1314 1331 const newContext = jQuery("#context").val();
1315 1332 const newConfig = contextConfigs[newContext];
1333 +
1316 1334 setInputsFromConfig(newContext, newConfig);
1317 1335 currentContext = newContext;
1318 1336 }
1319 1337
@@ -1914,16 +1932,15 @@
1914 1932 public function minicart_express_checkout_button() {
1915 1933 $gw = $this->gateway();
1916 1934
1917 1935 if ($gw->show_express_checkout()){
1918 - return $this->cart_express_checkout_button_html(true);
1936 + return $this->cart_express_checkout_button_html('minicart');
1919 1937 }
1920 1938 }
1921 1939
1922 - public function cart_express_checkout_button_html($minicart = false) {
1940 + public function cart_express_checkout_button_html($context= 'cart') {
1923 1941 $url = $this->express_checkout_url();
1924 1942 $url = wp_nonce_url($url,'express','sec');
1925 - $context = $minicart ? 'minicart' : 'cart';
1926 1943 $button= apply_filters('woo_vipps_express_checkout_button', $this->get_html_button_for_context($context));
1927 1944 $method = $this->get_payment_method_name();
1928 1945 $title = sprintf(__('Buy now with %1$s!', 'woo-vipps'), $method);
1929 1946 $html = "<a href='$url' class='vipps-express-checkout short $method' title='$title'>$button</a>";
@@ -1966,9 +1983,9 @@
1966 1983 public function express_checkout_button_shortcode() {
1967 1984 $gw = $this->gateway();
1968 1985 if (!$gw->cart_supports_express_checkout()) return;
1969 1986 ob_start();
1970 - $this->cart_express_checkout_button_html('shortcode');
1987 + $this->cart_express_checkout_button_html('cart');
1971 1988 return ob_get_clean();
1972 1989 }
1973 1990 // Show a banner normally shown for non-logged-in-users at the checkout page. It does not need to check if we are to show the button, obviously, but needs to see if the cart works
1974 1991 public function express_checkout_banner_shortcode() {
@@ -2649,12 +2666,30 @@
2649 2666 remove_filter('template_redirect', 'redirect_canonical', 10);
2650 2667 // dont cache special page. LP 2026-08-25
2651 2668 $this->nocache();
2652 2669 // Do the custom pre-load actions for these pages IOK 2026-09-11
2653 - do_action('woo_vipps_before_handling_special_page', $_GET['action']);
2670 + do_action('woo_vipps_before_handling_special_page', ($_GET['action'] ?? ""));
2654 2671 }
2655 2672 }
2656 2673
2674 + // Ran in template redirect for the special page. IOK 2026-09-2
2675 + public function pre_special_page_actions ($action) {
2676 + // Change title dynamically depending on action. LP 2026-09-02
2677 + add_filter('the_title', [$this, 'vipps_special_page_endpoint_title'], 10, 2);
2678 +
2679 + // If we are handling the 'wait for payment' action, we need to poll the order status before
2680 + // we start producing content IOK 2026-09-21
2681 + if ($action == 'wait_for_payment') {
2682 + $this->handle_payment_poll_and_redirect();
2683 + }
2684 +
2685 + // Some validation is required for this action
2686 + if ($action == 'do_express_checkout') {
2687 + $this->vipps_express_checkout_consistency_check();
2688 + }
2689 + }
2690 +
2691 +
2657 2692 // Dynamic special page title depending on endpoint/action, only frontend. LP 2026-09-02
2658 2693 public function vipps_special_page_endpoint_title($title, $postid = 0) {
2659 2694 global $wp_query;
2660 2695 // Comment from woocommerce's wc_page_endpoint_title where this logic is from: LP 2026-09-02
@@ -4133,9 +4168,9 @@
4133 4168 WC()->cart->calculate_totals();
4134 4169 WC()->cart->set_session();
4135 4170 return true;
4136 4171 } catch (Exception $e) {
4137 - $this->log(sprintf(__("Error regenerating cart from order %1\$d: %2\$s", 'woo-vipps'), $order_id, $e->get_message()), 'error');
4172 + $this->log(sprintf(__("Error regenerating cart from order %1\$d: %2\$s", 'woo-vipps'), $order_id, $e->getMessage()), 'error');
4138 4173 return false;
4139 4174 }
4140 4175 }
4141 4176
@@ -4245,9 +4280,11 @@
4245 4280 if ( empty($_REQUEST['add-to-cart']) || ! is_numeric($_REQUEST['add-to-cart']) || empty($_REQUEST['vipps_compat_mode']) || !$_REQUEST['vipps_compat_mode']) {
4246 4281 return $url;
4247 4282 }
4248 4283 $url = $this->express_checkout_url();
4249 - $url = wp_nonce_url($url,'express','sec');
4284 + // At this point, there is always a query argument here. IOK 2026-09-21
4285 + $nonce = wp_create_nonce('express');
4286 + $url = $url . "&sec=$nonce";
4250 4287
4251 4288 return $url;
4252 4289 }
4253 4290
@@ -4680,9 +4717,10 @@
4680 4717 }
4681 4718 if (!$o) return;
4682 4719 if (!$o->get_meta('_vipps_single_product_express')) return;
4683 4720 if ($failed && !apply_filters('woo_vipps_restore_cart_on_express_checkout_failure', true, $o)) return;
4684 - if ($failed) WC()->cart->empty_cart();
4721 + // Restoring cart! But clear it first so we dont add this single product to the restored cart. LP 2026-09-22
4722 + WC()->cart->empty_cart();
4685 4723 $this->restore_cart($o);
4686 4724 }
4687 4725
4688 4726
@@ -5389,15 +5427,13 @@
5389 5427
5390 5428 return $this->express_checkout_page_html(true,'do_single_product_express_checkout',$args);
5391 5429 }
5392 5430
5393 - // This is a landing page for the express checkout of then normal cart - it is done like this because this could take time on slower hosts.
5394 - public function vipps_express_checkout() {
5431 + public function vipps_express_checkout_consistency_check() {
5395 5432 // We need a nonce to get here, but we should only get here when we have a cart, so this will not be cached.
5396 5433 // IOK 2018-05-28
5397 5434 $ok = isset($_REQUEST['sec']) && wp_verify_nonce($_REQUEST['sec'],'express');
5398 5435
5399 -
5400 5436 $backurl = wp_validate_redirect(@$_SERVER['HTTP_REFERER']);
5401 5437 if (!$backurl) $backurl = home_url();
5402 5438
5403 5439 if (!$ok) {
@@ -5411,8 +5447,19 @@
5411 5447 wp_redirect($backurl);
5412 5448 exit();
5413 5449 }
5414 5450
5451 + add_filter('woo_vipps_express_checkout_consistent', '__return_true');
5452 + }
5453 +
5454 + // This is a landing page for the express checkout of then normal cart - it is done like this because this could take time on slower hosts.
5455 + public function vipps_express_checkout() {
5456 + // Some checks are made in template_redirect, we check here if they are ok IOK 2026-09-21
5457 + if (!apply_filters('woo_vipps_express_checkout_consistent', false)) {
5458 + $content = __('Link expired, please try again', 'woo-vipps');
5459 + return $content;
5460 + }
5461 +
5415 5462 add_filter('body_class', function ($classes) {
5416 5463 $classes[] = 'vipps-express-checkout';
5417 5464 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
5418 5465 return apply_filters('woo_vipps_express_checkout_body_class', $classes);
@@ -5616,12 +5663,11 @@
5616 5663 }
5617 5664 }
5618 5665
5619 5666
5620 -
5621 - public function vipps_wait_for_payment() {
5667 + // Called in template_redirect before we get to the wait-for-payment page IOK 2026-09-21
5668 + private function handle_payment_poll_and_redirect () {
5622 5669 $orderid = WC()->session->get('_vipps_pending_order');
5623 -
5624 5670 $order = null;
5625 5671 $gw = $this->gateway();
5626 5672
5627 5673 // Failsafe for when the session disappears IOK 2018-11-19
@@ -5633,9 +5679,9 @@
5633 5679 // If so, we will read the order id from the GET arguments and check if the auth token is correct,
5634 5680 // simulating the session with that.
5635 5681 // IOK 2019-11-19, changed to using GET 2023-01-23
5636 5682 if ($no_session && $limited_session) {
5637 - $orderid = intval(@$_GET['id']);
5683 + $orderid = intval($_GET['id'] ?? false);
5638 5684 }
5639 5685 if ($orderid) {
5640 5686 clean_post_cache($orderid);
5641 5687 $order = wc_get_order($orderid);
@@ -5654,10 +5700,8 @@
5654 5700 $session->set('_vipps_pending_order', $orderid);
5655 5701 }
5656 5702 }
5657 5703
5658 - do_action('woo_vipps_wait_for_payment_page',$order);
5659 -
5660 5704 $deleted_order=0;
5661 5705 if ($orderid && !$order) {
5662 5706 // If this happens, we actually did have an order, but it has been deleted, which must mean that it was cancelled.
5663 5707 // Concievably a hook on the 'cancel'-transition or in the callback handlers could clean that up before we get here. IOK 2019-09-26
@@ -5682,9 +5726,9 @@
5682 5726 clean_post_cache($orderid);
5683 5727 $order = wc_get_order($orderid); // Reload order object
5684 5728 }
5685 5729 } else {
5686 - // No need to do anyting here. IOK 2020-01-26
5730 + // No need to do anyting here. IOK 2020-01-26
5687 5731 }
5688 5732
5689 5733 $payment = 'notchecked';
5690 5734 if ($do_poll) {
@@ -5700,9 +5744,8 @@
5700 5744 exit();
5701 5745 }
5702 5746
5703 5747 // We are done, but in failure. Don't poll.
5704 - $content = "";
5705 5748 $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5706 5749
5707 5750 // Status is failed; still send to return url (as of now /order-recieved), the text there will depend on the status.
5708 5751 // For failed it shows a "Retry payment" button that takes the customer to /pay-for-order where it will be retried. LP 2026-03-17
@@ -5710,8 +5753,9 @@
5710 5753 $failure_redirect = $failure_redirect ?: $gw->get_return_url($order);
5711 5754 wp_redirect($failure_redirect);
5712 5755 exit();
5713 5756 }
5757 +
5714 5758 if ($status == 'cancelled' || $payment == 'cancelled') {
5715 5759 $this->maybe_restore_cart($orderid,'failed');
5716 5760 if ($failure_redirect){
5717 5761 wp_redirect($failure_redirect);
@@ -5716,8 +5760,37 @@
5716 5760 if ($failure_redirect){
5717 5761 wp_redirect($failure_redirect);
5718 5762 exit();
5719 5763 }
5764 + } else {
5765 + // If not, enqueue the status checker IOK 2026-09-21
5766 + wp_enqueue_script('check-vipps',plugins_url('js/check-order-status.js',__FILE__),array('jquery','vipps-gw'),filemtime(dirname(__FILE__) . "/js/check-order-status.js"), 'true');
5767 + }
5768 +
5769 + // Communicate this to the shortcode IOK 2026-09-21
5770 + add_filter('woo_vipps_wait_for_payment_status', function () use($orderid, $status, $payment) {
5771 + return ['orderid'=>$orderid, 'status'=>$status, 'payment'=>$payment];
5772 + });
5773 +
5774 + }
5775 +
5776 + public function vipps_wait_for_payment() {
5777 +
5778 + // This will have been computed in template_redirect, but the status will be either still pending or failed. IOK 2026-09-21
5779 + $data = apply_filters('woo_vipps_wait_for_payment_status', []);
5780 +
5781 + $orderid = $data['orderid'] ?? 0;
5782 + $status = $data['status'] ?? "";
5783 + $payment = $data['payment'] ?? "";
5784 +
5785 + $order = wc_get_order($orderid);
5786 + if (!$order) wp_die(__('Unknown order', 'woo-vipps'));
5787 +
5788 + do_action('woo_vipps_wait_for_payment_page',$order);
5789 + $gw = $this->gateway();
5790 +
5791 + $content = "";
5792 + if ($status == 'cancelled' || $payment == 'cancelled') {
5720 5793 $content .= "<div id=failure><p>". __('Order cancelled','woo-vipps') . '</p>';
5721 5794 $content .= "<p><a href='" . home_url() . "' class='btn button'>" . __('Continue shopping','woo-vipps') . '</a></p>';
5722 5795 $content .= "</div>";
5723 5796 return $this->special_page_html('', $content);
@@ -5723,12 +5796,9 @@
5723 5796 return $this->special_page_html('', $content);
5724 5797 }
5725 5798
5726 5799 // Still pending and order is supposed to exist, so wait for Vipps. This happens all the time, so logging is removed. IOK 2018-09-27
5727 -
5728 5800 // Otherwise, go to a page waiting/polling for the callback. IOK 2018-05-16
5729 - wp_enqueue_script('check-vipps',plugins_url('js/check-order-status.js',__FILE__),array('jquery','vipps-gw'),filemtime(dirname(__FILE__) . "/js/check-order-status.js"), 'true');
5730 -
5731 5801 $signal = $this->callbackSignal($order);
5732 5802 $content = "";
5733 5803 $content .= "<div id='waiting'><p>" . sprintf(__('Waiting for confirmation of purchase from %1$s','woo-vipps'), $this->get_payment_method_name());
5734 5804
@@ -5736,15 +5806,16 @@
5736 5806 $signalurl = $this->callbackSignalURL($signal);
5737 5807
5738 5808 $content .= "</p></div>";
5739 5809
5740 - // We impersonate the woocommerce-checkout form here mainly to work with the Pixel Your Site plugin IOK 2022-11-24
5741 - $classlist = apply_filters("woo_vipps_express_checkout_form_classes", "woocommerce-checkout");
5742 - $content .= "<form id='vippsdata' class='" . esc_attr($classlist) . "'>";
5810 + $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5811 +
5812 + // Carry the order status to the checking script IOK 2026-09-21
5813 + $content .= "<form id='vippsdata'>";
5743 5814 $content .= "<input type='hidden' id='fkey' name='fkey' value='".htmlspecialchars($signalurl)."'>";
5744 5815 $content .= "<input type='hidden' name='key' value='".htmlspecialchars($order->get_order_key())."'>";
5745 5816 $content .= "<input type='hidden' name='action' value='check_order_status'>";
5746 - $content .= wp_nonce_field('vippsstatus','sec',1,false);
5817 + $content .= wp_nonce_field('vippsstatus','sec',1,false);
5747 5818 $content .= "</form>";
5748 5819
5749 5820
5750 5821 $content .= "<div id='error' style='display:none'><p>".__('Error during order confirmation','woo-vipps'). '</p>';