PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.2.2
Pay with Vipps and MobilePay for WooCommerce v6.2.2
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 +348 -256 6.1.86.2.2 View file →
@@ -121,12 +121,17 @@
121 121 add_action('init',array($Vipps,'init'));
122 122 add_action( 'woocommerce_loaded', array($Vipps,'woocommerce_loaded'));
123 123 add_filter( 'woocommerce_available_payment_gateways', array($Vipps, 'payment_gateway_filter'));
124 124 add_action( 'woocommerce_blocks_loaded', [$Vipps, 'woocommerce_blocks_loaded']);
125 - // Express Checkout and Vipps Checkout supports the new pickup_location shipping method, but the admin interface for this may
125 + // Express Checkout and Checkout supports the new pickup_location shipping method, but the admin interface for this may
126 126 // not have loaded if the default checkout solution isn't the Checkout block. We'll load it anyway if the user has any local pickup locations
127 127 // stored in the database since we support this for both Vipps MobilePay checkokut and Express. IOK 2026-02-25
128 128 add_action('woocommerce_load_shipping_methods', array($Vipps, 'maybe_load_pickup_locations'), 90);
129 +
130 + // Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
131 + // Will also probably be used to maintain a real utility-page for Vipps actions later for themes where this
132 + // is important.
133 + add_filter('woocommerce_create_pages', array($Vipps, 'woocommerce_create_pages'), 50, 1);
129 134 }
130 135
131 136 // Register woocommerce store api endpoint to use in buy-now minicart block. LP 2026-02-10
132 137 public function woocommerce_blocks_loaded() {
@@ -280,8 +285,48 @@
280 285 }
281 286
282 287 // Set default button options, migrating any older setup IOK 2026-07-15
283 288 $this->init_button_options();
289 +
290 + /*
291 + From version 6.2.x we create a real physical page to handle the "special" vipps pages,
292 + where we earlier used just a fake page with no real page id, unless especially configured.
293 + We therefore need to add code to maintain this special page.
294 +
295 + woocommerce_loaded is too early for this because of maybe_create_vipps_pages which calls WC_Install::create_pages,
296 + and we hook unto this with woocommerce_create_pages. LP 2026-09-03
297 + */
298 +
299 + // Delete special page id option when its deleted or trashed, so that we dont have to load
300 + // in the post to check status in woocommerce_loaded when we ensure the special page exists. LP 2026-09-03
301 + $delete_special_page_id = function($post_id, $post = null) {
302 + if (static::get_special_page_id() === $post_id) {
303 + delete_option('woocommerce_vipps_special_page_page_id');
304 + }
305 + };
306 + add_action('delete_post', $delete_special_page_id, 10, 2);
307 + add_action('wp_trash_post', $delete_special_page_id, 10, 2);
308 +
309 + $this->ensure_special_page_exists();
310 +
311 +
312 + // We want this special page to have a certain title and maybe special scripts and so on,
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 +
318 + // If we are handling the 'wait for payment' action, we need to poll the order status before
319 + // we start producing content IOK 2026-09-21
320 + if ($action == 'wait_for_payment') {
321 + $this->handle_payment_poll_and_redirect();
322 + }
323 +
324 + });
325 +
326 + // Add an admin interface for this page as well IOK 2026-09-11
327 + add_action('woocommerce_settings_pages', array($this, 'woocommerce_settings_pages'));
328 +
284 329 }
285 330
286 331 public function admin_init () {
287 332 $gw = $this->gateway();
@@ -401,9 +446,77 @@
401 446 }
402 447 }
403 448 }
404 449
450 +
451 + /** Ensure we have a special page for payment flows
452 + *
453 + * woocommerce_loaded is too early for this because of maybe_create_vipps_pages which calls WC_Install::create_pages,
454 + * and we hook unto this with woocommerce_create_pages. LP 2026-09-03
455 + **/
456 + public function ensure_special_page_exists() {
457 + if (static::get_special_page_id()) return;
458 + $this->log(__('Missing id for special page, attempting to fix.', 'woo-vipps'), 'info');
405 459
460 + // If user had in previous version overriden the fake page with a real one: migrate this page to be the special page. LP 2026-09-01
461 + $old_special_page_id = $this->gateway()->get_option('vippsspecialpageid');
462 + if ($old_special_page_id && ($special_page = get_post($old_special_page_id)) && "trash" !== $special_page->post_status) {
463 + $this->log(__('Migrated old special page setting.', 'woo-vipps'), 'info');
464 + // there is no wc_set_page_id() so we update the option directly. LP 2026-09-01
465 + update_option('woocommerce_vipps_special_page_page_id', $old_special_page_id);
466 +
467 + // Ensure this page has the necessary shortcode. LP 2026-09-01
468 + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) {
469 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
470 + wp_update_post([
471 + 'ID' => $old_special_page_id,
472 + 'post_content' => $new_content,
473 + ]);
474 + }
475 + } else {
476 + // Create special page if its missing. LP 2026-09-01
477 + $this->maybe_create_vipps_pages();
478 + }
479 + }
480 +
481 + // Admin interface for the special page on woo/advanced/pages
482 + public function woocommerce_settings_pages ($settings) {
483 + $i = -1;
484 + foreach($settings as $entry) {
485 + $i++;
486 + if ($entry['type'] == 'sectionend' && $entry['id'] == 'advanced_page_options') {
487 + break;
488 + }
489 + }
490 + if ($i > 0) {
491 + $vippspagesettings = array(
492 + array(
493 + 'title' => sprintf(__( '%1$s Page', 'woo-vipps' ), Vipps::CompanyName()),
494 + 'desc' => sprintf(__('This page is used for various special pages used by %1$s', 'woo-vipps'), Vipps::CompanyName()) . sprintf( __( 'Page contents: [%1$s]', 'woocommerce' ), 'vipps_special_page') ,
495 + 'id' => 'woocommerce_vipps_special_page_page_id',
496 + 'type' => 'single_select_page_with_search',
497 + 'default' => '',
498 + 'class' => 'wc-page-search',
499 + 'css' => 'min-width:300px;',
500 + 'args' => array(
501 + 'exclude' =>
502 + array(
503 + wc_get_page_id( 'myaccount' ),
504 + wc_get_page_id( 'checkout' ),
505 + wc_get_page_id( 'cart' ),
506 + ),
507 + ),
508 + 'desc_tip' => true,
509 + 'autoload' => false,
510 + ));
511 + array_splice($settings, $i, 0, $vippspagesettings);
512 + }
513 +
514 + return $settings;
515 + }
516 +
517 +
518 +
406 519 // Runs on init, adds the Vipps badge feature if activated
407 520 public function maybe_add_vipps_badge_feature () {
408 521 $badge_options = get_option('vipps_badge_options');
409 522 if (!$badge_options || !@$badge_options['badgeon']) return false;
@@ -1164,9 +1277,9 @@
1164 1277
1165 1278 // Update the preview web component's attributes. LP 2026-06-24
1166 1279 function updatePreview(event) {
1167 1280 const args = getPreviewArgs();
1168 - // LP FIXME: when i use get_customer_language() here it gives me my user language, but on frontend it gives the site language, i.e not the same value. So this preview will be wrong language. so use get_locale for now. LP 2026-07-02
1281 + // FIXME: when i use get_customer_language() here it gives me my user language, but on frontend it gives the site language, i.e not the same value. So this preview will be wrong language. so use get_locale for now. LP 2026-07-02
1169 1282 // if ('store' === args.language) args.language = '<?php echo $this->get_customer_language(); ?>';
1170 1283 if ('store' === args.language) args.language = '<?php echo substr(get_locale(), 0, 2); ?>';
1171 1284 const button = jQuery('#vipps-button-express-preview');
1172 1285 button.attr(args);
@@ -1659,9 +1772,9 @@
1659 1772 [
1660 1773 'in_footer' => true,
1661 1774 'strategy' => 'async',
1662 1775 ],
1663 - );
1776 + );
1664 1777
1665 1778 // Button web component downloaded from https://cdn.vippsmobilepay.com/js/button/button.js. LP 2026-06-24
1666 1779 wp_register_script('vipps-button-webcomponent',
1667 1780 plugins_url('js/vipps-button.js', WC_VIPPS_PAYMENT_MAIN_FILE),
@@ -1667,10 +1780,9 @@
1667 1780 plugins_url('js/vipps-button.js', WC_VIPPS_PAYMENT_MAIN_FILE),
1668 1781 array(),
1669 1782 filemtime(dirname(WC_VIPPS_PAYMENT_MAIN_FILE) . '/js/vipps-button.js'),
1670 1783 [
1671 - 'in_footer' => true,
1672 - 'strategy' => 'async',
1784 + 'in_footer' => false
1673 1785 ],
1674 1786 );
1675 1787 }
1676 1788
@@ -1705,8 +1817,11 @@
1705 1817 // New vipps-mobilepay-badge shortcode. LP 19.11.2024
1706 1818 add_shortcode('vipps-mobilepay-badge', array($this, 'vipps_mobilepay_badge_shortcode'));
1707 1819 // Legacy vipps-badge shortcode. LP 19.11.2024
1708 1820 add_shortcode('vipps-badge', array($this, 'vipps_badge_shortcode'));
1821 +
1822 + // special page handling, previously a fake page. LP 2026-08-25
1823 + add_shortcode('vipps_special_page', array($this, 'vipps_special_page_shortcode'));
1709 1824 }
1710 1825
1711 1826
1712 1827 public function log ($what,$type='info') {
@@ -1732,8 +1847,10 @@
1732 1847 }
1733 1848
1734 1849 // Show express button option on checkout form. LP 2026-03-23
1735 1850 public function checkout_before_customer_details_express () {
1851 + if (did_action('woo_vipps_checkout_before_customer_details_express')) return;
1852 + do_action('woo_vipps_checkout_before_customer_details_express');
1736 1853 $gw = $this->gateway();
1737 1854 if (!$gw->show_express_checkout()) return;
1738 1855 $this->express_checkout_section_html();
1739 1856 }
@@ -2519,77 +2636,69 @@
2519 2636 return null;
2520 2637 }
2521 2638 }
2522 2639
2640 + // Special pages, and some callbacks. IOK 2018-05-18
2641 + public function template_redirect() {
2523 2642
2524 - // If this is a special page, return true very early because we are handling this. IOK 2023-02-22
2525 - public function pre_handle_404($current, $query) {
2526 - if (!is_admin()) {
2527 - $special = $this->is_special_page();
2528 - if ($special) {
2529 - // Ensure very early on that Autooptimize does not try to optimize us (if installed) IOK 2023-03-04
2530 - add_filter( 'autoptimize_filter_noptimize', '__return_true');
2531 - return true;
2532 - }
2643 + // Handle legacy vipps-buy-now urls that auto-start express checkout for certain product - in QR codes etc IOK 2026-09-11
2644 + // We redirect these to the new location.
2645 + $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
2646 + if (( ($_GET['VippsSpecialPage'] ?? '') == 'vipps-buy-product') || ($path && preg_match("!/vipps-buy-product/?$!", $path)) ) {
2647 + $url = static::get_special_page_url();
2648 + $_GET['action'] = 'buy_product';
2649 + $q = build_query($_GET);
2650 + wp_redirect($url . "?" . $q, 302);
2651 + exit();
2533 2652 }
2534 - return $current;
2535 - }
2536 2653
2537 - // Special pages, and some callbacks. IOK 2018-05-18
2538 - public function template_redirect() {
2539 - global $post;
2540 - // Handle special callbacks
2541 - $special = $this->is_special_page() ;
2542 -
2543 - if ($special) {
2654 + if (static::is_special_page()) {
2655 + // Legacy: Stop the canonical redirect here. Unclear if still necessary. IOK 2026-09-11
2544 2656 remove_filter('template_redirect', 'redirect_canonical', 10);
2545 - do_action('woo_vipps_before_handling_special_page', $special);
2657 + // dont cache special page. LP 2026-08-25
2658 + $this->nocache();
2659 + // Do the custom pre-load actions for these pages IOK 2026-09-11
2660 + do_action('woo_vipps_before_handling_special_page', ($_GET['action'] ?? ""));
2661 + }
2662 + }
2546 2663
2547 - // Allow above hook to actually handle special pages. It should probably call $Vipps->fakepage or a redirect; can be used
2548 - // to intercept express checkout etc. IOK 2022-03-18
2549 - if (! apply_filters('woo_vipps_special_page_handled', false, $special)) {
2550 - $this->$special();
2551 - }
2552 - }
2664 + // Dynamic special page title depending on endpoint/action, only frontend. LP 2026-09-02
2665 + public function vipps_special_page_endpoint_title($title, $postid = 0) {
2666 + global $wp_query;
2667 + // Comment from woocommerce's wc_page_endpoint_title where this logic is from: LP 2026-09-02
2553 2668
2554 - $consentremoval = $this->is_consent_removal();
2555 - if ($consentremoval) {
2556 - remove_filter('template_redirect', 'redirect_canonical', 10);
2557 - do_action('woo_vipps_before_handling_special_page', 'consentremoval');
2558 - if (! apply_filters('woo_vipps_special_page_handled', false, 'consentremoval')) {
2559 - $this->vipps_consent_removal_callback($consentremoval);
2669 + // In block themes the whole template (header, footer, content) renders inside the main
2670 + // loop, so `the_title` fires for any post title rendered on the page (e.g. a product in a
2671 + // server-rendered mini-cart) - not just the page's own heading. Only replace the title of
2672 + // the queried page so an earlier title doesn't consume this one-shot filter.
2673 + if ( ! is_null( $wp_query ) && ! is_admin() && is_main_query() && in_the_loop() && is_page() && $postid == static::get_special_page_id() ) {
2674 + switch ($_GET['action'] ?? '') {
2675 + case 'wait_for_payment':
2676 + $title = __('Processing order', 'woo-vipps');
2677 + break;
2678 + case 'do_express_checkout':
2679 + case 'buy_product':
2680 + $title = __('Express Checkout', 'woo-vipps');
2681 + break;
2560 2682 }
2561 2683 }
2684 + return $title;
2562 2685 }
2686 +
2563 2687 // Template handling for special pages. IOK 2018-11-21
2688 + // This is legacy - the special page is now a real page, so it can have a special template using standard WP methods. IOK 2026-09-11
2564 2689 public function template_include($template) {
2565 - $special = $this->is_special_page() ;
2566 - if ($special) {
2690 + if (static::is_special_page()) {
2567 2691 // Get any special template override from the options IOK 2020-02-18
2568 2692 $specific = $this->gateway()->get_option('vippsspecialpagetemplate');
2569 2693 $found = locate_template($specific,false,false);
2570 2694 if ($found) $template=$found;
2571 2695
2572 - return apply_filters('woo_vipps_special_page_template', $template, $special);
2696 + return apply_filters('woo_vipps_special_page_template', $template, $_GET['action'] ?? '');
2573 2697 }
2574 2698 return $template;
2575 2699 }
2576 2700
2577 -
2578 - // Can't use wc-api for this, as that does not support DELETE . IOK 2018-05-18
2579 - private function is_consent_removal () {
2580 -
2581 - if ($_SERVER['REQUEST_METHOD'] != 'DELETE') return false;
2582 - if ( !get_option('permalink_structure')) {
2583 - if (@$_REQUEST['vipps-consent-removal']) return @$_REQUEST['callback'];
2584 - return false;
2585 - }
2586 - if (preg_match("!/vipps-consent-removal/([^/]*)!", $_SERVER['REQUEST_URI'], $matches)) {
2587 - return @$_REQUEST['callback'];
2588 - }
2589 - return false;
2590 - }
2591 -
2592 2701 // On the thank you page, we have a completed order, so we need to restore any saved cart and possibly log in
2593 2702 // the user if using Express Checkout IOK 2020-10-09
2594 2703 public function woocommerce_before_thankyou ($orderid) {
2595 2704 $order = wc_get_order($orderid);
@@ -2594,9 +2703,9 @@
2594 2703 public function woocommerce_before_thankyou ($orderid) {
2595 2704 $order = wc_get_order($orderid);
2596 2705 if ($order) {
2597 2706 // Requires that this is express checkout and that 'create users on express checkout' is chosen. IOK 2020-10-09
2598 - // -- or the same thing for Vipps Checkout. Also, the NHG code should not be running, and there is a filter, too. IOK 2023-08-04
2707 + // -- or the same thing for Checkout. Also, the NHG code should not be running, and there is a filter, too. IOK 2023-08-04
2599 2708 $this->maybe_log_in_user($order);
2600 2709 $order->delete_meta_data('_vipps_limited_session');
2601 2710 $order->save();
2602 2711
@@ -2657,9 +2766,8 @@
2657 2766
2658 2767 // Support adding pickup locations to any shipping rate using the 'woo_vipps_shipping_method_pickup_points' filter
2659 2768 // IOK 2025-11-19
2660 2769 add_filter('woo_vipps_modify_express_checkout_rate', array($this, 'express_add_pickup_location_options'), 10, 4);
2661 -
2662 2770 }
2663 2771
2664 2772 public function get_payment_method_name() {
2665 2773 return $this->gateway()->get_option('payment_method_name');
@@ -2679,14 +2787,13 @@
2679 2787 public function after_setup_theme() {
2680 2788 // To facilitate development, allow loading the plugin-supplied translations. Must be called here at the earliest.
2681 2789 $ok = Vipps::load_plugin_textdomain('woo-vipps', false, basename( dirname( dirname( __FILE__ ) ) ) . "/languages");
2682 2790
2683 - // Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
2791 + // Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
2684 2792 // Will also probably be used to maintain a real utility-page for Vipps actions later for themes where this
2685 2793 // is important.
2686 2794 add_filter('woocommerce_create_pages', array($this, 'woocommerce_create_pages'), 50, 1);
2687 2795
2688 -
2689 2796 // Callbacks use the Woo API IOK 2018-05-18
2690 2797 add_action( 'woocommerce_api_wc_gateway_vipps', array($this,'vipps_callback'));
2691 2798 add_action( 'woocommerce_api_vipps_shipping_details', array($this,'vipps_shipping_details_callback'));
2692 2799
@@ -2697,9 +2804,9 @@
2697 2804 add_action( 'woocommerce_cart_actions', array($this, 'cart_express_checkout_button'));
2698 2805 add_action( 'woocommerce_widget_shopping_cart_buttons', array($this, 'minicart_express_checkout_button'), 30);
2699 2806
2700 2807 // Previously we added an express html banner to the action 'woocommerce_before_checkout_form.',
2701 - // replaced by the new express buttons in manner more like Gutenberg. LP 2026-03-23
2808 + // replaced by the new express buttons in manner more like Gutenberg. for grepping: "express legacy checkout". LP 2026-03-23
2702 2809 add_action('woocommerce_checkout_before_customer_details', array($this, 'checkout_before_customer_details_express'), 5);
2703 2810
2704 2811 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2705 2812 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
@@ -2704,12 +2811,10 @@
2704 2811 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2705 2812 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
2706 2813
2707 2814
2708 - // Special pages and callbacks handled by template_redirect
2709 - // We must also notify WP and other plugins that we are handling this 404-like situation. IOK 2023-02-22
2815 + // Special pages and callbacks handled by template_redirect. IOK 2023-02-22
2710 2816 add_action('template_redirect', array($this,'template_redirect'),1);
2711 - add_action('pre_handle_404', array($this, 'pre_handle_404'), 1, 2);
2712 2817
2713 2818 // Allow overriding their templates
2714 2819 add_filter('template_include', array($this,'template_include'), 10, 1);
2715 2820
@@ -2995,14 +3100,14 @@
2995 3100
2996 3101 $raw_post = @file_get_contents( 'php://input' );
2997 3102 $result = @json_decode($raw_post,true);
2998 3103
2999 - // This handler handles both Vipps Checkout and Vipps ECom IOK 2021-09-02
3104 + // This handler handles both Checkout and Vipps ECom IOK 2021-09-02
3000 3105 // .. and the epayment webhooks 2023-12-19
3001 3106 $ischeckout = false;
3002 3107 $iswebhook = false;
3003 3108 $callback = isset($_REQUEST['callback']) ? $_REQUEST['callback'] : "";
3004 - // For Vipps Checkout v3 and onwards, we control the callback so the type is just this field
3109 + // For Checkout v3 and onwards, we control the callback so the type is just this field
3005 3110 if ($callback == 'checkout') {
3006 3111 $ischeckout = true;
3007 3112 }
3008 3113 // For the webhooks, we will add 'webhook' to the result, but we also know that 'pspReference' will be present. IOK 2023-12-19
@@ -3416,10 +3521,10 @@
3416 3521 $this->log(sprintf(__("Wrong %1\$s Orderid on shipping details callback", 'woo-vipps'), $this->get_payment_method_name()), 'warning');
3417 3522 exit();
3418 3523 }
3419 3524
3420 - // If we are doing this for Vipps Checkout after version 3, communicate to any shipping methods with
3421 - // special support for Vipps Checkout that this is in fact happening. IOK 2023-01-19
3525 + // If we are doing this for Checkout after version 3, communicate to any shipping methods with
3526 + // special support for Checkout that this is in fact happening. IOK 2023-01-19
3422 3527 // This needs to be done before "calculate totals".
3423 3528 // Moved from "vipps_shipping_details_callback_handler" because we need it before restoring sessions. IOK 2025-05-06
3424 3529 $ischeckout = $order->get_meta('_vipps_checkout');
3425 3530
@@ -3595,9 +3700,9 @@
3595 3700 ), 'debug');
3596 3701
3597 3702 }
3598 3703
3599 - // Add shipping tax rates to the *order* so we can calculate this correctly when using Vipps Checkouts
3704 + // Add shipping tax rates to the *order* so we can calculate this correctly when using Checkouts
3600 3705 // 'dynamic pricing' 2023-01-26
3601 3706 // Which may be deprecated, but anyway, for future use IOK 2025-08-14
3602 3707 $taxrate = 0;
3603 3708 if (is_array($shipping_tax_rates) && !empty($shipping_tax_rates)) {
@@ -3723,9 +3828,9 @@
3723 3828 $vippsmethod['shippingMethod'] = $rate->get_label();
3724 3829 $vippsmethod['shippingMethodId'] = $key;
3725 3830 $vippsmethods[]=$vippsmethod;
3726 3831
3727 - // Metadata and settings stored for later use for Vipps Checkout
3832 + // Metadata and settings stored for later use for Checkout
3728 3833 // and express checkout - basically, for each *key* have the corresponding object. IOK 2025-08-15
3729 3834 // In the end, this data will be serialized and stored in the Order, and used in the gateways method set_order_shipping_details to
3730 3835 // finalize the order. IOK 2025-08-15
3731 3836 $ratemap[$key]=$rate;
@@ -3743,9 +3848,9 @@
3743 3848 // This then is the old Express Checkout format, which we have exposed in filters. IOK 2025-08-14
3744 3849 $return = array('addressId'=>intval($addressid), 'orderId'=>$vippsorderid, 'shippingDetails'=>$vippsmethods);
3745 3850 $return = apply_filters('woo_vipps_vipps_formatted_shipping_methods', $return); // Mostly for debugging
3746 3851
3747 - // IOK 2021-11-16 Vipps Checkout uses a slightly different syntax and format.
3852 + // IOK 2021-11-16 Checkout uses a slightly different syntax and format.
3748 3853 // IOK 2025-08-15 and new Express yet another slightly different format.
3749 3854 // IOK 2025-08-15 pass the ratemap as a reference, so transforms can update them
3750 3855 if ($ischeckout) {
3751 3856 $return = VippsCheckout::instance()->format_shipping_methods($return, $ratemap, $methodmap, $order);
@@ -4121,17 +4226,8 @@
4121 4226 header("X-Accel-Expires: 0");
4122 4227 }
4123 4228
4124 4229
4125 -
4126 - // Handle DELETE on a vipps consent removal callback
4127 - public function vipps_consent_removal_callback ($callback) {
4128 - Vipps::nocache();
4129 - // Currently, no such requests will be posted, and as this code isn't sufficiently tested,we'll just have
4130 - // to escape here when the API is changed. IOK 2020-10-14
4131 - $this->log("Consent removal is non-functional pending API changes as of 2020-10-14"); print "1"; exit();
4132 - }
4133 -
4134 4230 public function woocommerce_payment_gateways($methods) {
4135 4231 require_once(dirname(__FILE__) . "/WC_Gateway_Vipps.class.php");
4136 4232 require_once(dirname(__FILE__) . "/WC_Gateway_VippsCard.class.php");
4137 4233 // Protect the singleton: Use the object instead of the class name IOK 2025-02-04
@@ -4254,20 +4350,40 @@
4254 4350 }
4255 4351 }
4256 4352
4257 4353 public function activate () {
4258 - static::maybe_add_cron_event();
4259 - $gw = $this->gateway();
4354 + static::maybe_add_cron_event();
4355 + $gw = $this->gateway();
4260 4356
4261 - // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19
4262 - if ($gw->get_option('orderprefix') == 'Woo') {
4263 - $gw->update_option('orderprefix', $this->generate_order_prefix());
4264 - }
4265 - // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point.
4266 - $gw->initialize_webhooks();
4267 - $this->payment_method_name = $gw->get_option('payment_method_name');
4268 - }
4357 + // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19
4358 + if ($gw->get_option('orderprefix') == 'Woo') {
4359 + $gw->update_option('orderprefix', $this->generate_order_prefix());
4360 + }
4361 + // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point.
4362 + $gw->initialize_webhooks();
4363 + $this->payment_method_name = $gw->get_option('payment_method_name');
4269 4364
4365 +
4366 + // Check if the special page is noted and actually does exist
4367 + $special = static::get_special_page_id();
4368 + if ($special) {
4369 + $special_page = get_post($special);
4370 + if ($special_page && 'trash' !== $special_page->post_status) {
4371 + // Ensure this page has the necessary shortcode. LP 2026-09-01
4372 + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) {
4373 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
4374 + wp_update_post([
4375 + 'ID' => $special,
4376 + 'post_content' => $new_content,
4377 + ]);
4378 + }
4379 + } else {
4380 + delete_option('woocommerce_vipps_special_page_page_id');
4381 + }
4382 + }
4383 +
4384 + }
4385 +
4270 4386 // We have added some hooks to wp-cron; remove these. IOK 2020-04-01
4271 4387 public static function deactivate() {
4272 4388 $timestamp = wp_next_scheduled('vipps_cron_cleanup_hook');
4273 4389 wp_unschedule_event($timestamp, 'vipps_cron_cleanup_hook');
@@ -4320,9 +4436,10 @@
4320 4436 // If setting is true, use Vipps as default payment. Called by the woocommrece_cart_updated hook. IOK 2018-06-06
4321 4437 private function maybe_set_vipps_as_default() {
4322 4438 if (WC()->session->get('chosen_payment_method')) return; // User has already chosen payment method, so we're done.
4323 4439 $gw = $this->gateway();
4324 - if ($gw->get_option('vippsdefault')=='yes') {
4440 + // Do *not* default to vipps if Kustom Checkout is installed IOK 2026-09-11
4441 + if ($gw->get_option('vippsdefault')=='yes' && !class_exists('KCO')) {
4325 4442 WC()->session->set('chosen_payment_method', $gw->id);
4326 4443 }
4327 4444 }
4328 4445
@@ -4427,13 +4544,13 @@
4427 4544 if (is_user_logged_in()) return;
4428 4545 if (!$order || ! self::is_vipps_order($order)) return;
4429 4546
4430 4547 // We *do* want to log in express checkout customers, but not those that
4431 - // use the Vipps Checkout solution - those can change their emails in the
4548 + // use the Checkout solution - those can change their emails in the
4432 4549 // checkout screen. IOK 2021-09-03
4433 4550 $do_login = $order->get_meta('_vipps_express_checkout');
4434 4551
4435 - // We will not log in Vipps Checkout users unless the option for that is true
4552 + // We will not log in Checkout users unless the option for that is true
4436 4553 if ($order->get_meta('_vipps_checkout') && 'yes' != $this->gateway()->get_option('checkoutcreateuser')) {
4437 4554 $do_login = false;
4438 4555 }
4439 4556
@@ -4468,9 +4585,9 @@
4468 4585
4469 4586 // Both Checkout and Express Checkout have the below value set to true
4470 4587 if (!$order->get_meta('_vipps_express_checkout')) return;
4471 4588
4472 - // Creating/logging in users are handled separately for Vipps Checkout and Express Checkout, so check the correct setting
4589 + // Creating/logging in users are handled separately for Checkout and Express Checkout, so check the correct setting
4473 4590 // IOK 2023-07-27
4474 4591 $ischeckout = $order->get_meta('_vipps_checkout');
4475 4592 if ($ischeckout) {
4476 4593 if ($this->gateway()->get_option('checkoutcreateuser') != 'yes') return null;
@@ -4855,9 +4972,9 @@
4855 4972 $ok = wc()->shipping->register_shipping_method( new Automattic\WooCommerce\Blocks\Shipping\PickupLocation() );
4856 4973 }
4857 4974 }
4858 4975
4859 - // Vipps Checkout and Express Checkout allows loading specific kinds of shipping methods with non-standard APIs, such as PickupLocations. IOK 2025-05-08
4976 + // Checkout and Express Checkout allows loading specific kinds of shipping methods with non-standard APIs, such as PickupLocations. IOK 2025-05-08
4860 4977 // Must be called *early*. IOK 2025-05-08. Called in callback methods, and if using static shipping, in the 'start session' callback.
4861 4978 public function load_extra_shipping_methods($order, $addressdata, $ischeckout=false) {
4862 4979 // If we need to add more shipping methods *before* the shipping callback starts, it must be done before we load the session. IOK 2025-05-06
4863 4980 add_action('woocommerce_load_shipping_methods', function () use ($order, $addressdata) {
@@ -4928,46 +5045,37 @@
4928 5045 wp_send_json(array('status'=>'error', 'msg'=> __('Unknown payment status','woo-vipps') . ' ' . $payment));
4929 5046 return false;
4930 5047 }
4931 5048
4932 - // The various return URLs for special pages of the Vipps stuff depend on settings and pretty-URLs so we supply them from here
4933 - // These are for the "fallback URL" mostly. IOK 2018-05-18
4934 - private function make_vipps_url($what) {
4935 - if ( !get_option('permalink_structure')) {
4936 - return add_query_arg('VippsSpecialPage', $what, home_url("/", 'https'));
4937 - }
4938 - return trailingslashit(home_url($what, 'https'));
5049 + // The various return URLs for special pages of the Vipps stuff. Previously used a fake page and had to check permalink_structure. LP 2026-08-26
5050 + private function make_special_page_url($action) {
5051 + return add_query_arg('action', $action, $this->get_special_page_url());
4939 5052 }
5053 +
4940 5054 public function payment_return_url() {
4941 - return apply_filters('woo_vipps_payment_return_url', $this->make_vipps_url('vipps-betaling'));
5055 + return apply_filters('woo_vipps_payment_return_url', $this->make_special_page_url('wait_for_payment'));
4942 5056 }
4943 5057 public function express_checkout_url() {
4944 - return $this->make_vipps_url('vipps-express-checkout');
5058 + return $this->make_special_page_url('do_express_checkout');
4945 5059 }
4946 5060 public function buy_product_url() {
4947 - return $this->make_vipps_url('vipps-buy-product');
5061 + return $this->make_special_page_url('buy_product');
4948 5062 }
4949 5063
4950 - // Return the method in the Vipps
4951 - public function is_special_page() {
4952 - $specials = array('vipps-betaling' => 'vipps_wait_for_payment', 'vipps-express-checkout'=>'vipps_express_checkout', 'vipps-buy-product'=>'vipps_buy_product');
4953 - $method = null;
4954 - if ( get_option('permalink_structure')) {
4955 - foreach($specials as $special=>$specialmethod) {
4956 - // IOK 2018-06-07 Change to add any prefix from home-url for better matching IOK 2018-06-07
4957 - $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
4958 - if ($path && preg_match("!/$special/?$!", $path, $matches)) {
4959 - $method = $specialmethod; break;
4960 - }
4961 - }
4962 - } else {
4963 - if (isset($_GET['VippsSpecialPage'])) {
4964 - $method = @$specials[$_GET['VippsSpecialPage']];
4965 - }
4966 - }
4967 - return $method;
5064 + public static function is_special_page() {
5065 + $id = static::get_special_page_id();
5066 + return $id && is_page($id);
4968 5067 }
4969 5068
5069 + public static function get_special_page_id() {
5070 + $id = wc_get_page_id('vipps_special_page'); // -1 if not found
5071 + return $id > 0 ? $id : null;
5072 + }
5073 +
5074 + public static function get_special_page_url() {
5075 + return get_permalink(static::get_special_page_id());
5076 + }
5077 +
4970 5078 // Just create a spinner and a overlay.
4971 5079 public function spinner () {
4972 5080 $flavour = sanitize_title($this->get_payment_method_name());
4973 5081 ob_start();
@@ -5147,43 +5255,90 @@
5147 5255 }
5148 5256
5149 5257
5150 5258
5151 - // Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
5259 + // Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
5152 5260 // IOK 2026-04-30 remove this when checkout is end-of-life'd
5261 + // We now also use this for the vipps special page, previously a fakepage. LP 2026-08-18
5153 5262 public function woocommerce_create_pages ($data) {
5263 + // Vipps Checkout page
5154 5264 $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false);
5155 - if (!$vipps_checkout_activated) return $data;
5265 + if ($vipps_checkout_activated) {
5266 + $data['vipps_checkout'] = array(
5267 + 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ),
5268 + 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ),
5269 + 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->',
5270 + );
5271 + }
5156 5272
5157 - $data['vipps_checkout'] = array(
5158 - 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ),
5159 - 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ),
5160 - 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->',
5161 - );
5162 -
5273 + // Vipps special page for certain payment flow actions. Previously a fake page. LP 2026-08-18
5274 + $data['vipps_special_page'] = [
5275 + 'name' => 'vipps-payment', // slug
5276 + /* translators: company name */
5277 + 'title' => sprintf(__('%s special page', 'woo-vipps'), static::CompanyName()), // we hide the title frontend in template_redirect. LP 2026-08-27
5278 + 'content' => '<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->',
5279 + ];
5163 5280 return $data;
5164 5281 }
5165 5282
5166 - // Creates any necessary Vipps pages. Will be called e.g. when activating Vipps Checkout or turning it on.
5283 + // Creates any necessary Vipps pages. E.g vipps checkout page or vipps special page. LP 2026-09-01
5284 + // If a page slug already exists, then it won't overwrite or duplicate it!. LP 2026-09-02
5167 5285 public function maybe_create_vipps_pages () {
5286 + $make_pages = false;
5287 +
5288 + // Vipps Checkout page. LP 2026-08-18
5168 5289 $checkoutid = wc_get_page_id('vipps_checkout');
5169 - $makeit = !$checkoutid || ! get_post_status($checkoutid);
5170 - if ($makeit) {
5290 + if (!$checkoutid || ! get_post_status($checkoutid)) {
5171 5291 delete_option('woocommerce_vipps_checkout_page_id');
5292 + $make_pages = true;
5172 5293 }
5173 5294
5174 - if ($makeit) {
5175 - WC_Install::create_pages();
5295 + // vipps special page, previously a fake page. LP 2026-08-18
5296 + $builtin_special_page_id = static::get_special_page_id();
5297 + if (!$builtin_special_page_id || !get_post_status($builtin_special_page_id)) {
5298 + delete_option('woocommerce_vipps_special_page_page_id');
5299 + $make_pages = true;
5176 5300 }
5301 +
5302 + if ($make_pages) {
5303 + WC_Install::create_pages();
5304 + }
5177 5305 }
5178 5306
5307 + public function vipps_special_page_shortcode($atts, $content) {
5308 + // No point in expanding this unless we are actually doing the special actions. LP 2026-08-25
5309 + if (is_admin()) return;
5310 + if (wp_doing_ajax()) return;
5311 + if (defined('REST_REQUEST') && REST_REQUEST) return;
5312 + if (did_filter('woo_vipps_special_page_html')) return; // User has somehow added two shortcodes. IOK 2026-09-18
5179 5313
5314 + $action = $_GET['action'] ?? '';
5315 + $html = "";
5316 + switch ($action) {
5317 + case 'wait_for_payment':
5318 + $html = $this->vipps_wait_for_payment();
5319 + break;
5320 + case 'do_express_checkout':
5321 + $html = $this->vipps_express_checkout();
5322 + break;
5323 + case 'buy_product':
5324 + $html = $this->vipps_buy_product();
5325 + break;
5326 + default:
5327 + $html = '';
5328 + }
5329 + // This is mostly to avoid this shortcode evaluating twice IOK 2026-09-18
5330 + $html = apply_filters('woo_vipps_special_page_html', $html, $action);
5331 +
5332 + // Remember, this is a shortcode, so the html must be returned, not echoed IOK 2026-09-11
5333 + return $html;
5334 + }
5335 +
5336 +
5180 5337 // This URL will when accessed add a product to the cart and go directly to the express checkout page.
5181 5338 // The argument passed must be a shareable link created for a given product - so this in effect acts as a landing page for
5182 5339 // the buying thru Vipps Express Checkout of a single product linked to in for instance banners. IOK 2018-09-24
5183 5340 public function vipps_buy_product() {
5184 - status_header(200,'OK');
5185 - Vipps::nocache();
5186 5341
5187 5342 add_filter('body_class', function ($classes) {
5188 5343 $classes[] = 'vipps-express-checkout';
5189 5344 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
@@ -5219,9 +5374,9 @@
5219 5374
5220 5375 if (!$productinfo) {
5221 5376 $title = __("Product is no longer available",'woo-vipps');
5222 5377 $content = __("The link you have followed is for a product that is no longer available at this location. Please return to the store and try again",'woo-vipps');
5223 - return $this->fakepage($title,$content);
5378 + return $this->special_page_html($title,$content);
5224 5379 }
5225 5380
5226 5381 // Pass the productinfo to the express checkout form
5227 5382 $args = array();
@@ -5238,15 +5393,13 @@
5238 5393 }
5239 5394 $args[sanitize_title(wp_unslash($key))] = sanitize_text_field(wp_unslash($value));
5240 5395 }
5241 5396
5242 - $this->print_express_checkout_page(true,'do_single_product_express_checkout',$args);
5397 + return $this->express_checkout_page_html(true,'do_single_product_express_checkout',$args);
5243 5398 }
5244 5399
5245 5400 // 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.
5246 5401 public function vipps_express_checkout() {
5247 - status_header(200,'OK');
5248 - Vipps::nocache();
5249 5402 // We need a nonce to get here, but we should only get here when we have a cart, so this will not be cached.
5250 5403 // IOK 2018-05-28
5251 5404 $ok = isset($_REQUEST['sec']) && wp_verify_nonce($_REQUEST['sec'],'express');
5252 5405
@@ -5273,9 +5426,9 @@
5273 5426 });
5274 5427
5275 5428 do_action('woo_vipps_express_checkout_page');
5276 5429
5277 - $this->print_express_checkout_page(true, 'do_express_checkout');
5430 + return $this->express_checkout_page_html(true, 'do_express_checkout');
5278 5431 }
5279 5432
5280 5433 // This method tries to ensure that a customer does not 'lose' the return page and
5281 5434 // starts ordering the same products twice. IOK 2020-01-22
@@ -5370,9 +5523,10 @@
5370 5523 return $orderspec;
5371 5524 }
5372 5525
5373 5526 // Used as a landing page for launching express checkout - borh for the cart and for single products. IOK 2018-09-28
5374 - protected function print_express_checkout_page($execute,$action,$productinfo=null) {
5527 + // Returns the html. LP 2026-08-27
5528 + protected function express_checkout_page_html($execute,$action,$productinfo=null) {
5375 5529 $gw = $this->gateway();
5376 5530
5377 5531 $expressCheckoutMessages = array();
5378 5532 $expressCheckoutMessages['termsAndConditionsError'] = __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' );
@@ -5455,10 +5609,9 @@
5455 5609
5456 5610 if ($execute) {
5457 5611 $content .= "<p id=waiting>" . __("Please wait while we are preparing your order", 'woo-vipps') . "</p>";
5458 5612 $content .= "<div id='vipps-status-message'></div>";
5459 - $this->fakepage(__('Order in progress','woo-vipps'), $content);
5460 - return;
5613 + return $this->special_page_html('', $content);
5461 5614 } else {
5462 5615 $content .= $askForConfirmationHTML;
5463 5616 $content .= $extraHTML;
5464 5617 $content .= $termsHTML;
@@ -5465,21 +5618,16 @@
5465 5618 $content .= apply_filters('woo_vipps_express_checkout_validation_elements', '');
5466 5619 $title = sprintf(__('Buy now with %1$s!', 'woo-vipps'), $this->get_payment_method_name());
5467 5620 $content .= "<div class='vipps_buy_now_wrapper noloop'><a href='#' id='do-express-checkout' class='vipps-express-checkout' title='$title'>$buttonhtml</a></div>";
5468 5621 $content .= "<div id='vipps-status-message'></div>";
5469 - $this->fakepage(sprintf(__('%1$s Express Checkout','woo-vipps'), $this->get_payment_method_name()), $content);
5470 - return;
5622 + return $this->special_page_html('', $content);
5471 5623 }
5472 5624 }
5473 5625
5474 5626
5475 -
5476 - public function vipps_wait_for_payment() {
5477 - status_header(200,'OK');
5478 - Vipps::nocache();
5479 -
5627 + // Called in template_redirect before we get to the wait-for-payment page IOK 2026-09-21
5628 + private function handle_payment_poll_and_redirect () {
5480 5629 $orderid = WC()->session->get('_vipps_pending_order');
5481 -
5482 5630 $order = null;
5483 5631 $gw = $this->gateway();
5484 5632
5485 5633 // Failsafe for when the session disappears IOK 2018-11-19
@@ -5491,9 +5639,9 @@
5491 5639 // If so, we will read the order id from the GET arguments and check if the auth token is correct,
5492 5640 // simulating the session with that.
5493 5641 // IOK 2019-11-19, changed to using GET 2023-01-23
5494 5642 if ($no_session && $limited_session) {
5495 - $orderid = intval(@$_GET['id']);
5643 + $orderid = intval($_GET['id'] ?? false);
5496 5644 }
5497 5645 if ($orderid) {
5498 5646 clean_post_cache($orderid);
5499 5647 $order = wc_get_order($orderid);
@@ -5512,11 +5660,8 @@
5512 5660 $session->set('_vipps_pending_order', $orderid);
5513 5661 }
5514 5662 }
5515 5663
5516 -
5517 - do_action('woo_vipps_wait_for_payment_page',$order);
5518 -
5519 5664 $deleted_order=0;
5520 5665 if ($orderid && !$order) {
5521 5666 // If this happens, we actually did have an order, but it has been deleted, which must mean that it was cancelled.
5522 5667 // Concievably a hook on the 'cancel'-transition or in the callback handlers could clean that up before we get here. IOK 2019-09-26
@@ -5541,9 +5686,9 @@
5541 5686 clean_post_cache($orderid);
5542 5687 $order = wc_get_order($orderid); // Reload order object
5543 5688 }
5544 5689 } else {
5545 - // No need to do anyting here. IOK 2020-01-26
5690 + // No need to do anyting here. IOK 2020-01-26
5546 5691 }
5547 5692
5548 5693 $payment = 'notchecked';
5549 5694 if ($do_poll) {
@@ -5559,9 +5704,8 @@
5559 5704 exit();
5560 5705 }
5561 5706
5562 5707 // We are done, but in failure. Don't poll.
5563 - $content = "";
5564 5708 $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5565 5709
5566 5710 // Status is failed; still send to return url (as of now /order-recieved), the text there will depend on the status.
5567 5711 // 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
@@ -5569,8 +5713,9 @@
5569 5713 $failure_redirect = $failure_redirect ?: $gw->get_return_url($order);
5570 5714 wp_redirect($failure_redirect);
5571 5715 exit();
5572 5716 }
5717 +
5573 5718 if ($status == 'cancelled' || $payment == 'cancelled') {
5574 5719 $this->maybe_restore_cart($orderid,'failed');
5575 5720 if ($failure_redirect){
5576 5721 wp_redirect($failure_redirect);
@@ -5575,27 +5720,45 @@
5575 5720 if ($failure_redirect){
5576 5721 wp_redirect($failure_redirect);
5577 5722 exit();
5578 5723 }
5724 + } else {
5725 + // If not, enqueue the status checker IOK 2026-09-21
5726 + 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');
5727 + }
5728 +
5729 + // Communicate this to the shortcode IOK 2026-09-21
5730 + add_filter('woo_vipps_wait_for_payment_status', function () use($orderid, $status, $payment) {
5731 + return ['orderid'=>$orderid, 'status'=>$status, 'payment'=>$payment];
5732 + });
5733 +
5734 + }
5735 +
5736 + public function vipps_wait_for_payment() {
5737 +
5738 + // This will have been computed in template_redirect, but the status will be either still pending or failed. IOK 2026-09-21
5739 + $data = apply_filters('woo_vipps_wait_for_payment_status', []);
5740 +
5741 + $orderid = $data['orderid'] ?? 0;
5742 + $status = $data['status'] ?? "";
5743 + $payment = $data['payment'] ?? "";
5744 +
5745 + $order = wc_get_order($orderid);
5746 + if (!$order) wp_die(__('Unknown order', 'woo-vipps'));
5747 +
5748 + do_action('woo_vipps_wait_for_payment_page',$order);
5749 + $gw = $this->gateway();
5750 +
5751 + $content = "";
5752 + if ($status == 'cancelled' || $payment == 'cancelled') {
5579 5753 $content .= "<div id=failure><p>". __('Order cancelled','woo-vipps') . '</p>';
5580 5754 $content .= "<p><a href='" . home_url() . "' class='btn button'>" . __('Continue shopping','woo-vipps') . '</a></p>';
5581 5755 $content .= "</div>";
5582 - $this->fakepage(__('Order cancelled','woo-vipps'), $content);
5583 -
5584 - return;
5756 + return $this->special_page_html('', $content);
5585 5757 }
5586 5758
5587 5759 // 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
5588 -
5589 5760 // Otherwise, go to a page waiting/polling for the callback. IOK 2018-05-16
5590 - 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');
5591 -
5592 - // Check that order exists and belongs to our session. Can use WC()->session->get() I guess - set the orderid or a hash value in the session
5593 - // and check that the order matches (and is 'pending') (and exists)
5594 - $vippsstamp = $order->get_meta('_vipps_init_timestamp');
5595 - $vippsstatus = $order->get_meta('_vipps_status');
5596 - $message = __($order->get_meta('_vipps_confirm_message'),'woo-vipps');
5597 -
5598 5761 $signal = $this->callbackSignal($order);
5599 5762 $content = "";
5600 5763 $content .= "<div id='waiting'><p>" . sprintf(__('Waiting for confirmation of purchase from %1$s','woo-vipps'), $this->get_payment_method_name());
5601 5764
@@ -5603,15 +5766,16 @@
5603 5766 $signalurl = $this->callbackSignalURL($signal);
5604 5767
5605 5768 $content .= "</p></div>";
5606 5769
5607 - // We impersonate the woocommerce-checkout form here mainly to work with the Pixel Your Site plugin IOK 2022-11-24
5608 - $classlist = apply_filters("woo_vipps_express_checkout_form_classes", "woocommerce-checkout");
5609 - $content .= "<form id='vippsdata' class='" . esc_attr($classlist) . "'>";
5770 + $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5771 +
5772 + // Carry the order status to the checking script IOK 2026-09-21
5773 + $content .= "<form id='vippsdata'>";
5610 5774 $content .= "<input type='hidden' id='fkey' name='fkey' value='".htmlspecialchars($signalurl)."'>";
5611 5775 $content .= "<input type='hidden' name='key' value='".htmlspecialchars($order->get_order_key())."'>";
5612 5776 $content .= "<input type='hidden' name='action' value='check_order_status'>";
5613 - $content .= wp_nonce_field('vippsstatus','sec',1,false);
5777 + $content .= wp_nonce_field('vippsstatus','sec',1,false);
5614 5778 $content .= "</form>";
5615 5779
5616 5780
5617 5781 $content .= "<div id='error' style='display:none'><p>".__('Error during order confirmation','woo-vipps'). '</p>';
@@ -5628,93 +5792,21 @@
5628 5792 $content .= "<a id='continueToOrderFailed' style='display:none' href='" . $failure_redirect . "'></a>";
5629 5793 $content .= "<a id='continueToOrderFailedFallback' style='display:none' href='" . $gw->get_return_url($order) . "'></a>";
5630 5794 $content .= "</div>";
5631 5795
5796 + return $this->special_page_html('', $content);
5797 + }
5632 5798
5633 - $this->fakepage(__('Waiting for your order confirmation','woo-vipps'), $content);
5799 + // Returns formatted html for the vipps special page. LP 2026-08-27
5800 + public function special_page_html($header, $content) {
5801 + $header_html = $header ? "<h2 class='vipps-special-page-title page-title'>$header</h2>" : '';
5802 + $html = <<<EOF
5803 + $header_html
5804 + <div class="vipps-special-page-content">$content</div>
5805 + EOF;
5806 + return apply_filters('woo_vipps_special_page_html', $html, $header, $content);
5634 5807 }
5635 5808
5636 -
5637 -
5638 - public function fakepage($title,$content) {
5639 - global $wp, $wp_query;
5640 - // We don't want this here.
5641 - remove_filter ('the_content', 'wpautop');
5642 -
5643 - $specialid = $this->gateway()->get_option('vippsspecialpageid');
5644 - $wp_post = null;
5645 - if ($specialid) {
5646 - $wp_post = get_post($specialid);
5647 - if ($wp_post) {
5648 - $wp_post->post_title = $title;
5649 - $wp_post->post_content = $content;
5650 - // Normalize a bit
5651 - $wp_post->filter = 'raw'; // important
5652 - $wp_post->post_status = 'publish';
5653 - $wp_post->comment_status= 'closed';
5654 - $wp_post->ping_status= 'closed';
5655 - } else {
5656 - $this->log(sprintf(__("Could not use special page with id %s - it seems not to exist.", 'woo-vipps'), $specialid), 'error');
5657 - }
5658 - }
5659 - if (!$wp_post || is_wp_error($wp_post)) {
5660 - $post = new stdClass();
5661 - $post->ID = -99;
5662 - $post->post_author = 1;
5663 - $post->post_date = current_time( 'mysql' );
5664 - $post->post_date_gmt = current_time( 'mysql', 1 );
5665 - $post->post_title = $title;
5666 - $post->post_content = $content;
5667 - $post->post_status = 'publish';
5668 - $post->comment_status = 'closed';
5669 - $post->ping_status = 'closed';
5670 - $post->post_name = 'vippsconfirm-fake-page-name';
5671 - $post->post_type = 'page';
5672 - $post->filter = 'raw'; // important
5673 - $wp_post = new WP_Post($post);
5674 - wp_cache_add( -99, $wp_post, 'posts' );
5675 - }
5676 -
5677 - // Update the main query
5678 - $wp_query->post = $wp_post;
5679 - $wp_query->posts = array( $wp_post );
5680 - $wp_query->queried_object = $wp_post;
5681 - $wp_query->queried_object_id = $wp_post->ID;
5682 - $wp_query->found_posts = 1;
5683 - $wp_query->post_count = 1;
5684 - $wp_query->max_num_pages = 1;
5685 - $wp_query->is_page = true;
5686 - $wp_query->is_singular = true;
5687 - $wp_query->is_single = false;
5688 - $wp_query->is_attachment = false;
5689 - $wp_query->is_archive = false;
5690 - $wp_query->is_category = false;
5691 - $wp_query->is_tag = false;
5692 - $wp_query->is_tax = false;
5693 - $wp_query->is_author = false;
5694 - $wp_query->is_date = false;
5695 - $wp_query->is_year = false;
5696 - $wp_query->is_month = false;
5697 - $wp_query->is_day = false;
5698 - $wp_query->is_time = false;
5699 - $wp_query->is_search = false;
5700 - $wp_query->is_feed = false;
5701 - $wp_query->is_comment_feed = false;
5702 - $wp_query->is_trackback = false;
5703 - $wp_query->is_home = false;
5704 - $wp_query->is_embed = false;
5705 - $wp_query->is_404 = false;
5706 - $wp_query->is_paged = false;
5707 - $wp_query->is_admin = false;
5708 - $wp_query->is_preview = false;
5709 - $wp_query->is_robots = false;
5710 - $wp_query->is_posts_page = false;
5711 - $wp_query->is_post_type_archive = false;
5712 - // Update globals
5713 - $GLOBALS['wp_query'] = $wp_query;
5714 - $wp->register_globals();
5715 - return $wp_post;
5716 - }
5717 5809
5718 5810 // Support the interactivity API with data about our cart IOK 2026-02-23
5719 5811 public function woo_vipps_store_api_cart_data() {
5720 5812 // Reverting the condition with the directive data-wp-bind--hidden does not work, so we need the flipped bool here (hide instead of show). LP 2026-02-10