PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.2.3
Pay with Vipps and MobilePay for WooCommerce v6.2.3
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 5.4.1 5.4.0 All 185 releases
← All changes | payment/Vipps.class.php +82 -25 6.2.06.2.3 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
@@ -1840,8 +1837,10 @@
1840 1837 }
1841 1838
1842 1839 // Show express button option on checkout form. LP 2026-03-23
1843 1840 public function checkout_before_customer_details_express () {
1841 + if (did_action('woo_vipps_checkout_before_customer_details_express')) return;
1842 + do_action('woo_vipps_checkout_before_customer_details_express');
1844 1843 $gw = $this->gateway();
1845 1844 if (!$gw->show_express_checkout()) return;
1846 1845 $this->express_checkout_section_html();
1847 1846 }
@@ -2647,12 +2646,30 @@
2647 2646 remove_filter('template_redirect', 'redirect_canonical', 10);
2648 2647 // dont cache special page. LP 2026-08-25
2649 2648 $this->nocache();
2650 2649 // Do the custom pre-load actions for these pages IOK 2026-09-11
2651 - do_action('woo_vipps_before_handling_special_page', $_GET['action']);
2650 + do_action('woo_vipps_before_handling_special_page', ($_GET['action'] ?? ""));
2652 2651 }
2653 2652 }
2654 2653
2654 + // Ran in template redirect for the special page. IOK 2026-09-2
2655 + public function pre_special_page_actions ($action) {
2656 + // Change title dynamically depending on action. LP 2026-09-02
2657 + add_filter('the_title', [$this, 'vipps_special_page_endpoint_title'], 10, 2);
2658 +
2659 + // If we are handling the 'wait for payment' action, we need to poll the order status before
2660 + // we start producing content IOK 2026-09-21
2661 + if ($action == 'wait_for_payment') {
2662 + $this->handle_payment_poll_and_redirect();
2663 + }
2664 +
2665 + // Some validation is required for this action
2666 + if ($action == 'do_express_checkout') {
2667 + $this->vipps_express_checkout_consistency_check();
2668 + }
2669 + }
2670 +
2671 +
2655 2672 // Dynamic special page title depending on endpoint/action, only frontend. LP 2026-09-02
2656 2673 public function vipps_special_page_endpoint_title($title, $postid = 0) {
2657 2674 global $wp_query;
2658 2675 // Comment from woocommerce's wc_page_endpoint_title where this logic is from: LP 2026-09-02
@@ -2795,9 +2812,9 @@
2795 2812 add_action( 'woocommerce_cart_actions', array($this, 'cart_express_checkout_button'));
2796 2813 add_action( 'woocommerce_widget_shopping_cart_buttons', array($this, 'minicart_express_checkout_button'), 30);
2797 2814
2798 2815 // Previously we added an express html banner to the action 'woocommerce_before_checkout_form.',
2799 - // replaced by the new express buttons in manner more like Gutenberg. LP 2026-03-23
2816 + // replaced by the new express buttons in manner more like Gutenberg. for grepping: "express legacy checkout". LP 2026-03-23
2800 2817 add_action('woocommerce_checkout_before_customer_details', array($this, 'checkout_before_customer_details_express'), 5);
2801 2818
2802 2819 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2803 2820 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
@@ -4243,9 +4260,11 @@
4243 4260 if ( empty($_REQUEST['add-to-cart']) || ! is_numeric($_REQUEST['add-to-cart']) || empty($_REQUEST['vipps_compat_mode']) || !$_REQUEST['vipps_compat_mode']) {
4244 4261 return $url;
4245 4262 }
4246 4263 $url = $this->express_checkout_url();
4247 - $url = wp_nonce_url($url,'express','sec');
4264 + // At this point, there is always a query argument here. IOK 2026-09-21
4265 + $nonce = wp_create_nonce('express');
4266 + $url = $url . "&sec=$nonce";
4248 4267
4249 4268 return $url;
4250 4269 }
4251 4270
@@ -5299,10 +5318,12 @@
5299 5318 // No point in expanding this unless we are actually doing the special actions. LP 2026-08-25
5300 5319 if (is_admin()) return;
5301 5320 if (wp_doing_ajax()) return;
5302 5321 if (defined('REST_REQUEST') && REST_REQUEST) return;
5322 + if (did_filter('woo_vipps_special_page_html')) return; // User has somehow added two shortcodes. IOK 2026-09-18
5303 5323
5304 5324 $action = $_GET['action'] ?? '';
5325 + $html = "";
5305 5326 switch ($action) {
5306 5327 case 'wait_for_payment':
5307 5328 $html = $this->vipps_wait_for_payment();
5308 5329 break;
@@ -5314,8 +5335,10 @@
5314 5335 break;
5315 5336 default:
5316 5337 $html = '';
5317 5338 }
5339 + // This is mostly to avoid this shortcode evaluating twice IOK 2026-09-18
5340 + $html = apply_filters('woo_vipps_special_page_html', $html, $action);
5318 5341
5319 5342 // Remember, this is a shortcode, so the html must be returned, not echoed IOK 2026-09-11
5320 5343 return $html;
5321 5344 }
@@ -5324,8 +5347,9 @@
5324 5347 // This URL will when accessed add a product to the cart and go directly to the express checkout page.
5325 5348 // The argument passed must be a shareable link created for a given product - so this in effect acts as a landing page for
5326 5349 // the buying thru Vipps Express Checkout of a single product linked to in for instance banners. IOK 2018-09-24
5327 5350 public function vipps_buy_product() {
5351 +
5328 5352 add_filter('body_class', function ($classes) {
5329 5353 $classes[] = 'vipps-express-checkout';
5330 5354 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
5331 5355 return apply_filters('woo_vipps_express_checkout_body_class', $classes);
@@ -5382,15 +5406,13 @@
5382 5406
5383 5407 return $this->express_checkout_page_html(true,'do_single_product_express_checkout',$args);
5384 5408 }
5385 5409
5386 - // 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.
5387 - public function vipps_express_checkout() {
5410 + public function vipps_express_checkout_consistency_check() {
5388 5411 // We need a nonce to get here, but we should only get here when we have a cart, so this will not be cached.
5389 5412 // IOK 2018-05-28
5390 5413 $ok = isset($_REQUEST['sec']) && wp_verify_nonce($_REQUEST['sec'],'express');
5391 5414
5392 -
5393 5415 $backurl = wp_validate_redirect(@$_SERVER['HTTP_REFERER']);
5394 5416 if (!$backurl) $backurl = home_url();
5395 5417
5396 5418 if (!$ok) {
@@ -5404,8 +5426,19 @@
5404 5426 wp_redirect($backurl);
5405 5427 exit();
5406 5428 }
5407 5429
5430 + add_filter('woo_vipps_express_checkout_consistent', '__return_true');
5431 + }
5432 +
5433 + // 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.
5434 + public function vipps_express_checkout() {
5435 + // Some checks are made in template_redirect, we check here if they are ok IOK 2026-09-21
5436 + if (!apply_filters('woo_vipps_express_checkout_consistent', false)) {
5437 + $content = __('Link expired, please try again', 'woo-vipps');
5438 + return $content;
5439 + }
5440 +
5408 5441 add_filter('body_class', function ($classes) {
5409 5442 $classes[] = 'vipps-express-checkout';
5410 5443 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
5411 5444 return apply_filters('woo_vipps_express_checkout_body_class', $classes);
@@ -5609,12 +5642,11 @@
5609 5642 }
5610 5643 }
5611 5644
5612 5645
5613 -
5614 - public function vipps_wait_for_payment() {
5646 + // Called in template_redirect before we get to the wait-for-payment page IOK 2026-09-21
5647 + private function handle_payment_poll_and_redirect () {
5615 5648 $orderid = WC()->session->get('_vipps_pending_order');
5616 -
5617 5649 $order = null;
5618 5650 $gw = $this->gateway();
5619 5651
5620 5652 // Failsafe for when the session disappears IOK 2018-11-19
@@ -5626,9 +5658,9 @@
5626 5658 // If so, we will read the order id from the GET arguments and check if the auth token is correct,
5627 5659 // simulating the session with that.
5628 5660 // IOK 2019-11-19, changed to using GET 2023-01-23
5629 5661 if ($no_session && $limited_session) {
5630 - $orderid = intval(@$_GET['id']);
5662 + $orderid = intval($_GET['id'] ?? false);
5631 5663 }
5632 5664 if ($orderid) {
5633 5665 clean_post_cache($orderid);
5634 5666 $order = wc_get_order($orderid);
@@ -5647,10 +5679,8 @@
5647 5679 $session->set('_vipps_pending_order', $orderid);
5648 5680 }
5649 5681 }
5650 5682
5651 - do_action('woo_vipps_wait_for_payment_page',$order);
5652 -
5653 5683 $deleted_order=0;
5654 5684 if ($orderid && !$order) {
5655 5685 // If this happens, we actually did have an order, but it has been deleted, which must mean that it was cancelled.
5656 5686 // Concievably a hook on the 'cancel'-transition or in the callback handlers could clean that up before we get here. IOK 2019-09-26
@@ -5675,9 +5705,9 @@
5675 5705 clean_post_cache($orderid);
5676 5706 $order = wc_get_order($orderid); // Reload order object
5677 5707 }
5678 5708 } else {
5679 - // No need to do anyting here. IOK 2020-01-26
5709 + // No need to do anyting here. IOK 2020-01-26
5680 5710 }
5681 5711
5682 5712 $payment = 'notchecked';
5683 5713 if ($do_poll) {
@@ -5693,9 +5723,8 @@
5693 5723 exit();
5694 5724 }
5695 5725
5696 5726 // We are done, but in failure. Don't poll.
5697 - $content = "";
5698 5727 $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5699 5728
5700 5729 // Status is failed; still send to return url (as of now /order-recieved), the text there will depend on the status.
5701 5730 // 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
@@ -5703,8 +5732,9 @@
5703 5732 $failure_redirect = $failure_redirect ?: $gw->get_return_url($order);
5704 5733 wp_redirect($failure_redirect);
5705 5734 exit();
5706 5735 }
5736 +
5707 5737 if ($status == 'cancelled' || $payment == 'cancelled') {
5708 5738 $this->maybe_restore_cart($orderid,'failed');
5709 5739 if ($failure_redirect){
5710 5740 wp_redirect($failure_redirect);
@@ -5709,8 +5739,37 @@
5709 5739 if ($failure_redirect){
5710 5740 wp_redirect($failure_redirect);
5711 5741 exit();
5712 5742 }
5743 + } else {
5744 + // If not, enqueue the status checker IOK 2026-09-21
5745 + 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');
5746 + }
5747 +
5748 + // Communicate this to the shortcode IOK 2026-09-21
5749 + add_filter('woo_vipps_wait_for_payment_status', function () use($orderid, $status, $payment) {
5750 + return ['orderid'=>$orderid, 'status'=>$status, 'payment'=>$payment];
5751 + });
5752 +
5753 + }
5754 +
5755 + public function vipps_wait_for_payment() {
5756 +
5757 + // This will have been computed in template_redirect, but the status will be either still pending or failed. IOK 2026-09-21
5758 + $data = apply_filters('woo_vipps_wait_for_payment_status', []);
5759 +
5760 + $orderid = $data['orderid'] ?? 0;
5761 + $status = $data['status'] ?? "";
5762 + $payment = $data['payment'] ?? "";
5763 +
5764 + $order = wc_get_order($orderid);
5765 + if (!$order) wp_die(__('Unknown order', 'woo-vipps'));
5766 +
5767 + do_action('woo_vipps_wait_for_payment_page',$order);
5768 + $gw = $this->gateway();
5769 +
5770 + $content = "";
5771 + if ($status == 'cancelled' || $payment == 'cancelled') {
5713 5772 $content .= "<div id=failure><p>". __('Order cancelled','woo-vipps') . '</p>';
5714 5773 $content .= "<p><a href='" . home_url() . "' class='btn button'>" . __('Continue shopping','woo-vipps') . '</a></p>';
5715 5774 $content .= "</div>";
5716 5775 return $this->special_page_html('', $content);
@@ -5716,12 +5775,9 @@
5716 5775 return $this->special_page_html('', $content);
5717 5776 }
5718 5777
5719 5778 // 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
5720 -
5721 5779 // Otherwise, go to a page waiting/polling for the callback. IOK 2018-05-16
5722 - 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');
5723 -
5724 5780 $signal = $this->callbackSignal($order);
5725 5781 $content = "";
5726 5782 $content .= "<div id='waiting'><p>" . sprintf(__('Waiting for confirmation of purchase from %1$s','woo-vipps'), $this->get_payment_method_name());
5727 5783
@@ -5729,15 +5785,16 @@
5729 5785 $signalurl = $this->callbackSignalURL($signal);
5730 5786
5731 5787 $content .= "</p></div>";
5732 5788
5733 - // We impersonate the woocommerce-checkout form here mainly to work with the Pixel Your Site plugin IOK 2022-11-24
5734 - $classlist = apply_filters("woo_vipps_express_checkout_form_classes", "woocommerce-checkout");
5735 - $content .= "<form id='vippsdata' class='" . esc_attr($classlist) . "'>";
5789 + $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5790 +
5791 + // Carry the order status to the checking script IOK 2026-09-21
5792 + $content .= "<form id='vippsdata'>";
5736 5793 $content .= "<input type='hidden' id='fkey' name='fkey' value='".htmlspecialchars($signalurl)."'>";
5737 5794 $content .= "<input type='hidden' name='key' value='".htmlspecialchars($order->get_order_key())."'>";
5738 5795 $content .= "<input type='hidden' name='action' value='check_order_status'>";
5739 - $content .= wp_nonce_field('vippsstatus','sec',1,false);
5796 + $content .= wp_nonce_field('vippsstatus','sec',1,false);
5740 5797 $content .= "</form>";
5741 5798
5742 5799
5743 5800 $content .= "<div id='error' style='display:none'><p>".__('Error during order confirmation','woo-vipps'). '</p>';