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 +378 -246 6.1.10 → 6.2.5 View file →
@@ -125,8 +125,13 @@
125 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,38 @@
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', array($this, 'pre_special_page_actions'));
315 +
316 + // Add an admin interface for this page as well IOK 2026-09-11
317 + add_action('woocommerce_settings_pages', array($this, 'woocommerce_settings_pages'));
318 +
284 319 }
285 320
286 321 public function admin_init () {
287 322 $gw = $this->gateway();
@@ -401,9 +436,77 @@
401 436 }
402 437 }
403 438 }
404 439
440 +
441 + /** Ensure we have a special page for payment flows
442 + *
443 + * woocommerce_loaded is too early for this because of maybe_create_vipps_pages which calls WC_Install::create_pages,
444 + * and we hook unto this with woocommerce_create_pages. LP 2026-09-03
445 + **/
446 + public function ensure_special_page_exists() {
447 + if (static::get_special_page_id()) return;
448 + $this->log(__('Missing id for special page, attempting to fix.', 'woo-vipps'), 'info');
405 449
450 + // 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
451 + $old_special_page_id = $this->gateway()->get_option('vippsspecialpageid');
452 + if ($old_special_page_id && ($special_page = get_post($old_special_page_id)) && "trash" !== $special_page->post_status) {
453 + $this->log(__('Migrated old special page setting.', 'woo-vipps'), 'info');
454 + // there is no wc_set_page_id() so we update the option directly. LP 2026-09-01
455 + update_option('woocommerce_vipps_special_page_page_id', $old_special_page_id);
456 +
457 + // Ensure this page has the necessary shortcode. LP 2026-09-01
458 + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) {
459 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
460 + wp_update_post([
461 + 'ID' => $old_special_page_id,
462 + 'post_content' => $new_content,
463 + ]);
464 + }
465 + } else {
466 + // Create special page if its missing. LP 2026-09-01
467 + $this->maybe_create_vipps_pages();
468 + }
469 + }
470 +
471 + // Admin interface for the special page on woo/advanced/pages
472 + public function woocommerce_settings_pages ($settings) {
473 + $i = -1;
474 + foreach($settings as $entry) {
475 + $i++;
476 + if ($entry['type'] == 'sectionend' && $entry['id'] == 'advanced_page_options') {
477 + break;
478 + }
479 + }
480 + if ($i > 0) {
481 + $vippspagesettings = array(
482 + array(
483 + 'title' => sprintf(__( '%1$s Page', 'woo-vipps' ), Vipps::CompanyName()),
484 + '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') ,
485 + 'id' => 'woocommerce_vipps_special_page_page_id',
486 + 'type' => 'single_select_page_with_search',
487 + 'default' => '',
488 + 'class' => 'wc-page-search',
489 + 'css' => 'min-width:300px;',
490 + 'args' => array(
491 + 'exclude' =>
492 + array(
493 + wc_get_page_id( 'myaccount' ),
494 + wc_get_page_id( 'checkout' ),
495 + wc_get_page_id( 'cart' ),
496 + ),
497 + ),
498 + 'desc_tip' => true,
499 + 'autoload' => false,
500 + ));
501 + array_splice($settings, $i, 0, $vippspagesettings);
502 + }
503 +
504 + return $settings;
505 + }
506 +
507 +
508 +
406 509 // Runs on init, adds the Vipps badge feature if activated
407 510 public function maybe_add_vipps_badge_feature () {
408 511 $badge_options = get_option('vipps_badge_options');
409 512 if (!$badge_options || !@$badge_options['badgeon']) return false;
@@ -940,12 +1043,30 @@
940 1043
941 1044 public function get_html_button_attrs_for_context($context = 'global') {
942 1045 $options = get_option('vipps_button_options2', []);
943 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 +
944 1059 $config = $options['express']['configs'][$context] ?? [];
945 - if (!$config || ($config['use-global-config'] ?? false)) {
1060 + $use_global = !$config || ($config['use-global-config'] ?? false);
1061 + if ($use_global) {
946 1062 $config = $options['express']['configs']['global'] ?? $this->get_html_button_default_attrs();
947 1063 }
1064 +
1065 + // see above.
1066 + if ($gutenberg) {
1067 + $config['stretched']='true';
1068 + }
948 1069 return $config;
949 1070 }
950 1071
951 1072 public function get_html_button_for_context($context = 'global') {
@@ -1032,8 +1153,10 @@
1032 1153 private function button_menu_express_section() {
1033 1154 $options = get_option('vipps_button_options2', []);
1034 1155 $express = $options['express'] ?? [];
1035 1156 $configs = $express['configs'] ?? [];
1157 +
1158 +
1036 1159 $contexts = [
1037 1160 'global' => __('Global', 'woo-vipps'),
1038 1161 'product' => __('Product', 'woo-vipps'),
1039 1162 'catalog' => __('Catalog', 'woo-vipps'),
@@ -1164,9 +1287,9 @@
1164 1287
1165 1288 // Update the preview web component's attributes. LP 2026-06-24
1166 1289 function updatePreview(event) {
1167 1290 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
1291 + // 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 1292 // if ('store' === args.language) args.language = '<?php echo $this->get_customer_language(); ?>';
1170 1293 if ('store' === args.language) args.language = '<?php echo substr(get_locale(), 0, 2); ?>';
1171 1294 const button = jQuery('#vipps-button-express-preview');
1172 1295 button.attr(args);
@@ -1206,8 +1329,9 @@
1206 1329
1207 1330 // Swap to new context: set all input fields to the stored values if exists. LP 2026-06-25
1208 1331 const newContext = jQuery("#context").val();
1209 1332 const newConfig = contextConfigs[newContext];
1333 +
1210 1334 setInputsFromConfig(newContext, newConfig);
1211 1335 currentContext = newContext;
1212 1336 }
1213 1337
@@ -1704,8 +1828,11 @@
1704 1828 // New vipps-mobilepay-badge shortcode. LP 19.11.2024
1705 1829 add_shortcode('vipps-mobilepay-badge', array($this, 'vipps_mobilepay_badge_shortcode'));
1706 1830 // Legacy vipps-badge shortcode. LP 19.11.2024
1707 1831 add_shortcode('vipps-badge', array($this, 'vipps_badge_shortcode'));
1832 +
1833 + // special page handling, previously a fake page. LP 2026-08-25
1834 + add_shortcode('vipps_special_page', array($this, 'vipps_special_page_shortcode'));
1708 1835 }
1709 1836
1710 1837
1711 1838 public function log ($what,$type='info') {
@@ -1731,8 +1858,10 @@
1731 1858 }
1732 1859
1733 1860 // Show express button option on checkout form. LP 2026-03-23
1734 1861 public function checkout_before_customer_details_express () {
1862 + if (did_action('woo_vipps_checkout_before_customer_details_express')) return;
1863 + do_action('woo_vipps_checkout_before_customer_details_express');
1735 1864 $gw = $this->gateway();
1736 1865 if (!$gw->show_express_checkout()) return;
1737 1866 $this->express_checkout_section_html();
1738 1867 }
@@ -1803,16 +1932,15 @@
1803 1932 public function minicart_express_checkout_button() {
1804 1933 $gw = $this->gateway();
1805 1934
1806 1935 if ($gw->show_express_checkout()){
1807 - return $this->cart_express_checkout_button_html(true);
1936 + return $this->cart_express_checkout_button_html('minicart');
1808 1937 }
1809 1938 }
1810 1939
1811 - public function cart_express_checkout_button_html($minicart = false) {
1940 + public function cart_express_checkout_button_html($context= 'cart') {
1812 1941 $url = $this->express_checkout_url();
1813 1942 $url = wp_nonce_url($url,'express','sec');
1814 - $context = $minicart ? 'minicart' : 'cart';
1815 1943 $button= apply_filters('woo_vipps_express_checkout_button', $this->get_html_button_for_context($context));
1816 1944 $method = $this->get_payment_method_name();
1817 1945 $title = sprintf(__('Buy now with %1$s!', 'woo-vipps'), $method);
1818 1946 $html = "<a href='$url' class='vipps-express-checkout short $method' title='$title'>$button</a>";
@@ -1855,9 +1983,9 @@
1855 1983 public function express_checkout_button_shortcode() {
1856 1984 $gw = $this->gateway();
1857 1985 if (!$gw->cart_supports_express_checkout()) return;
1858 1986 ob_start();
1859 - $this->cart_express_checkout_button_html('shortcode');
1987 + $this->cart_express_checkout_button_html('cart');
1860 1988 return ob_get_clean();
1861 1989 }
1862 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
1863 1991 public function express_checkout_banner_shortcode() {
@@ -2518,77 +2646,87 @@
2518 2646 return null;
2519 2647 }
2520 2648 }
2521 2649
2650 + // Special pages, and some callbacks. IOK 2018-05-18
2651 + public function template_redirect() {
2522 2652
2523 - // If this is a special page, return true very early because we are handling this. IOK 2023-02-22
2524 - public function pre_handle_404($current, $query) {
2525 - if (!is_admin()) {
2526 - $special = $this->is_special_page();
2527 - if ($special) {
2528 - // Ensure very early on that Autooptimize does not try to optimize us (if installed) IOK 2023-03-04
2529 - add_filter( 'autoptimize_filter_noptimize', '__return_true');
2530 - return true;
2531 - }
2653 + // Handle legacy vipps-buy-now urls that auto-start express checkout for certain product - in QR codes etc IOK 2026-09-11
2654 + // We redirect these to the new location.
2655 + $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
2656 + if (( ($_GET['VippsSpecialPage'] ?? '') == 'vipps-buy-product') || ($path && preg_match("!/vipps-buy-product/?$!", $path)) ) {
2657 + $url = static::get_special_page_url();
2658 + $_GET['action'] = 'buy_product';
2659 + $q = build_query($_GET);
2660 + wp_redirect($url . "?" . $q, 302);
2661 + exit();
2532 2662 }
2533 - return $current;
2663 +
2664 + if (static::is_special_page()) {
2665 + // Legacy: Stop the canonical redirect here. Unclear if still necessary. IOK 2026-09-11
2666 + remove_filter('template_redirect', 'redirect_canonical', 10);
2667 + // dont cache special page. LP 2026-08-25
2668 + $this->nocache();
2669 + // Do the custom pre-load actions for these pages IOK 2026-09-11
2670 + do_action('woo_vipps_before_handling_special_page', ($_GET['action'] ?? ""));
2671 + }
2534 2672 }
2535 2673
2536 - // Special pages, and some callbacks. IOK 2018-05-18
2537 - public function template_redirect() {
2538 - global $post;
2539 - // Handle special callbacks
2540 - $special = $this->is_special_page() ;
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);
2541 2678
2542 - if ($special) {
2543 - remove_filter('template_redirect', 'redirect_canonical', 10);
2544 - do_action('woo_vipps_before_handling_special_page', $special);
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 + }
2545 2684
2546 - // Allow above hook to actually handle special pages. It should probably call $Vipps->fakepage or a redirect; can be used
2547 - // to intercept express checkout etc. IOK 2022-03-18
2548 - if (! apply_filters('woo_vipps_special_page_handled', false, $special)) {
2549 - $this->$special();
2550 - }
2685 + // Some validation is required for this action
2686 + if ($action == 'do_express_checkout') {
2687 + $this->vipps_express_checkout_consistency_check();
2551 2688 }
2689 + }
2552 2690
2553 - $consentremoval = $this->is_consent_removal();
2554 - if ($consentremoval) {
2555 - remove_filter('template_redirect', 'redirect_canonical', 10);
2556 - do_action('woo_vipps_before_handling_special_page', 'consentremoval');
2557 - if (! apply_filters('woo_vipps_special_page_handled', false, 'consentremoval')) {
2558 - $this->vipps_consent_removal_callback($consentremoval);
2691 +
2692 + // Dynamic special page title depending on endpoint/action, only frontend. LP 2026-09-02
2693 + public function vipps_special_page_endpoint_title($title, $postid = 0) {
2694 + global $wp_query;
2695 + // Comment from woocommerce's wc_page_endpoint_title where this logic is from: LP 2026-09-02
2696 +
2697 + // In block themes the whole template (header, footer, content) renders inside the main
2698 + // loop, so `the_title` fires for any post title rendered on the page (e.g. a product in a
2699 + // server-rendered mini-cart) - not just the page's own heading. Only replace the title of
2700 + // the queried page so an earlier title doesn't consume this one-shot filter.
2701 + if ( ! is_null( $wp_query ) && ! is_admin() && is_main_query() && in_the_loop() && is_page() && $postid == static::get_special_page_id() ) {
2702 + switch ($_GET['action'] ?? '') {
2703 + case 'wait_for_payment':
2704 + $title = __('Processing order', 'woo-vipps');
2705 + break;
2706 + case 'do_express_checkout':
2707 + case 'buy_product':
2708 + $title = __('Express Checkout', 'woo-vipps');
2709 + break;
2559 2710 }
2560 2711 }
2712 + return $title;
2561 2713 }
2714 +
2562 2715 // Template handling for special pages. IOK 2018-11-21
2716 + // 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
2563 2717 public function template_include($template) {
2564 - $special = $this->is_special_page() ;
2565 - if ($special) {
2718 + if (static::is_special_page()) {
2566 2719 // Get any special template override from the options IOK 2020-02-18
2567 2720 $specific = $this->gateway()->get_option('vippsspecialpagetemplate');
2568 2721 $found = locate_template($specific,false,false);
2569 2722 if ($found) $template=$found;
2570 2723
2571 - return apply_filters('woo_vipps_special_page_template', $template, $special);
2724 + return apply_filters('woo_vipps_special_page_template', $template, $_GET['action'] ?? '');
2572 2725 }
2573 2726 return $template;
2574 2727 }
2575 2728
2576 -
2577 - // Can't use wc-api for this, as that does not support DELETE . IOK 2018-05-18
2578 - private function is_consent_removal () {
2579 -
2580 - if ($_SERVER['REQUEST_METHOD'] != 'DELETE') return false;
2581 - if ( !get_option('permalink_structure')) {
2582 - if (@$_REQUEST['vipps-consent-removal']) return @$_REQUEST['callback'];
2583 - return false;
2584 - }
2585 - if (preg_match("!/vipps-consent-removal/([^/]*)!", $_SERVER['REQUEST_URI'], $matches)) {
2586 - return @$_REQUEST['callback'];
2587 - }
2588 - return false;
2589 - }
2590 -
2591 2729 // On the thank you page, we have a completed order, so we need to restore any saved cart and possibly log in
2592 2730 // the user if using Express Checkout IOK 2020-10-09
2593 2731 public function woocommerce_before_thankyou ($orderid) {
2594 2732 $order = wc_get_order($orderid);
@@ -2656,9 +2794,8 @@
2656 2794
2657 2795 // Support adding pickup locations to any shipping rate using the 'woo_vipps_shipping_method_pickup_points' filter
2658 2796 // IOK 2025-11-19
2659 2797 add_filter('woo_vipps_modify_express_checkout_rate', array($this, 'express_add_pickup_location_options'), 10, 4);
2660 -
2661 2798 }
2662 2799
2663 2800 public function get_payment_method_name() {
2664 2801 return $this->gateway()->get_option('payment_method_name');
@@ -2683,9 +2820,8 @@
2683 2820 // Will also probably be used to maintain a real utility-page for Vipps actions later for themes where this
2684 2821 // is important.
2685 2822 add_filter('woocommerce_create_pages', array($this, 'woocommerce_create_pages'), 50, 1);
2686 2823
2687 -
2688 2824 // Callbacks use the Woo API IOK 2018-05-18
2689 2825 add_action( 'woocommerce_api_wc_gateway_vipps', array($this,'vipps_callback'));
2690 2826 add_action( 'woocommerce_api_vipps_shipping_details', array($this,'vipps_shipping_details_callback'));
2691 2827
@@ -2696,9 +2832,9 @@
2696 2832 add_action( 'woocommerce_cart_actions', array($this, 'cart_express_checkout_button'));
2697 2833 add_action( 'woocommerce_widget_shopping_cart_buttons', array($this, 'minicart_express_checkout_button'), 30);
2698 2834
2699 2835 // Previously we added an express html banner to the action 'woocommerce_before_checkout_form.',
2700 - // replaced by the new express buttons in manner more like Gutenberg. LP 2026-03-23
2836 + // replaced by the new express buttons in manner more like Gutenberg. for grepping: "express legacy checkout". LP 2026-03-23
2701 2837 add_action('woocommerce_checkout_before_customer_details', array($this, 'checkout_before_customer_details_express'), 5);
2702 2838
2703 2839 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2704 2840 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
@@ -2703,12 +2839,10 @@
2703 2839 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2704 2840 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
2705 2841
2706 2842
2707 - // Special pages and callbacks handled by template_redirect
2708 - // We must also notify WP and other plugins that we are handling this 404-like situation. IOK 2023-02-22
2843 + // Special pages and callbacks handled by template_redirect. IOK 2023-02-22
2709 2844 add_action('template_redirect', array($this,'template_redirect'),1);
2710 - add_action('pre_handle_404', array($this, 'pre_handle_404'), 1, 2);
2711 2845
2712 2846 // Allow overriding their templates
2713 2847 add_filter('template_include', array($this,'template_include'), 10, 1);
2714 2848
@@ -4034,9 +4168,9 @@
4034 4168 WC()->cart->calculate_totals();
4035 4169 WC()->cart->set_session();
4036 4170 return true;
4037 4171 } catch (Exception $e) {
4038 - $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');
4039 4173 return false;
4040 4174 }
4041 4175 }
4042 4176
@@ -4120,17 +4254,8 @@
4120 4254 header("X-Accel-Expires: 0");
4121 4255 }
4122 4256
4123 4257
4124 -
4125 - // Handle DELETE on a vipps consent removal callback
4126 - public function vipps_consent_removal_callback ($callback) {
4127 - Vipps::nocache();
4128 - // Currently, no such requests will be posted, and as this code isn't sufficiently tested,we'll just have
4129 - // to escape here when the API is changed. IOK 2020-10-14
4130 - $this->log("Consent removal is non-functional pending API changes as of 2020-10-14"); print "1"; exit();
4131 - }
4132 -
4133 4258 public function woocommerce_payment_gateways($methods) {
4134 4259 require_once(dirname(__FILE__) . "/WC_Gateway_Vipps.class.php");
4135 4260 require_once(dirname(__FILE__) . "/WC_Gateway_VippsCard.class.php");
4136 4261 // Protect the singleton: Use the object instead of the class name IOK 2025-02-04
@@ -4155,9 +4280,11 @@
4155 4280 if ( empty($_REQUEST['add-to-cart']) || ! is_numeric($_REQUEST['add-to-cart']) || empty($_REQUEST['vipps_compat_mode']) || !$_REQUEST['vipps_compat_mode']) {
4156 4281 return $url;
4157 4282 }
4158 4283 $url = $this->express_checkout_url();
4159 - $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";
4160 4287
4161 4288 return $url;
4162 4289 }
4163 4290
@@ -4253,20 +4380,40 @@
4253 4380 }
4254 4381 }
4255 4382
4256 4383 public function activate () {
4257 - static::maybe_add_cron_event();
4258 - $gw = $this->gateway();
4384 + static::maybe_add_cron_event();
4385 + $gw = $this->gateway();
4259 4386
4260 - // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19
4261 - if ($gw->get_option('orderprefix') == 'Woo') {
4262 - $gw->update_option('orderprefix', $this->generate_order_prefix());
4263 - }
4264 - // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point.
4265 - $gw->initialize_webhooks();
4266 - $this->payment_method_name = $gw->get_option('payment_method_name');
4267 - }
4387 + // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19
4388 + if ($gw->get_option('orderprefix') == 'Woo') {
4389 + $gw->update_option('orderprefix', $this->generate_order_prefix());
4390 + }
4391 + // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point.
4392 + $gw->initialize_webhooks();
4393 + $this->payment_method_name = $gw->get_option('payment_method_name');
4268 4394
4395 +
4396 + // Check if the special page is noted and actually does exist
4397 + $special = static::get_special_page_id();
4398 + if ($special) {
4399 + $special_page = get_post($special);
4400 + if ($special_page && 'trash' !== $special_page->post_status) {
4401 + // Ensure this page has the necessary shortcode. LP 2026-09-01
4402 + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) {
4403 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
4404 + wp_update_post([
4405 + 'ID' => $special,
4406 + 'post_content' => $new_content,
4407 + ]);
4408 + }
4409 + } else {
4410 + delete_option('woocommerce_vipps_special_page_page_id');
4411 + }
4412 + }
4413 +
4414 + }
4415 +
4269 4416 // We have added some hooks to wp-cron; remove these. IOK 2020-04-01
4270 4417 public static function deactivate() {
4271 4418 $timestamp = wp_next_scheduled('vipps_cron_cleanup_hook');
4272 4419 wp_unschedule_event($timestamp, 'vipps_cron_cleanup_hook');
@@ -4570,9 +4717,10 @@
4570 4717 }
4571 4718 if (!$o) return;
4572 4719 if (!$o->get_meta('_vipps_single_product_express')) return;
4573 4720 if ($failed && !apply_filters('woo_vipps_restore_cart_on_express_checkout_failure', true, $o)) return;
4574 - 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();
4575 4723 $this->restore_cart($o);
4576 4724 }
4577 4725
4578 4726
@@ -4928,46 +5076,37 @@
4928 5076 wp_send_json(array('status'=>'error', 'msg'=> __('Unknown payment status','woo-vipps') . ' ' . $payment));
4929 5077 return false;
4930 5078 }
4931 5079
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'));
5080 + // 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
5081 + private function make_special_page_url($action) {
5082 + return add_query_arg('action', $action, $this->get_special_page_url());
4939 5083 }
5084 +
4940 5085 public function payment_return_url() {
4941 - return apply_filters('woo_vipps_payment_return_url', $this->make_vipps_url('vipps-betaling'));
5086 + return apply_filters('woo_vipps_payment_return_url', $this->make_special_page_url('wait_for_payment'));
4942 5087 }
4943 5088 public function express_checkout_url() {
4944 - return $this->make_vipps_url('vipps-express-checkout');
5089 + return $this->make_special_page_url('do_express_checkout');
4945 5090 }
4946 5091 public function buy_product_url() {
4947 - return $this->make_vipps_url('vipps-buy-product');
5092 + return $this->make_special_page_url('buy_product');
4948 5093 }
4949 5094
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;
5095 + public static function is_special_page() {
5096 + $id = static::get_special_page_id();
5097 + return $id && is_page($id);
4968 5098 }
4969 5099
5100 + public static function get_special_page_id() {
5101 + $id = wc_get_page_id('vipps_special_page'); // -1 if not found
5102 + return $id > 0 ? $id : null;
5103 + }
5104 +
5105 + public static function get_special_page_url() {
5106 + return get_permalink(static::get_special_page_id());
5107 + }
5108 +
4970 5109 // Just create a spinner and a overlay.
4971 5110 public function spinner () {
4972 5111 $flavour = sanitize_title($this->get_payment_method_name());
4973 5112 ob_start();
@@ -5149,41 +5288,88 @@
5149 5288
5150 5289
5151 5290 // Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
5152 5291 // IOK 2026-04-30 remove this when checkout is end-of-life'd
5292 + // We now also use this for the vipps special page, previously a fakepage. LP 2026-08-18
5153 5293 public function woocommerce_create_pages ($data) {
5294 + // Vipps Checkout page
5154 5295 $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false);
5155 - if (!$vipps_checkout_activated) return $data;
5296 + if ($vipps_checkout_activated) {
5297 + $data['vipps_checkout'] = array(
5298 + 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ),
5299 + 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ),
5300 + 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->',
5301 + );
5302 + }
5156 5303
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 -
5304 + // Vipps special page for certain payment flow actions. Previously a fake page. LP 2026-08-18
5305 + $data['vipps_special_page'] = [
5306 + 'name' => 'vipps-payment', // slug
5307 + /* translators: company name */
5308 + 'title' => sprintf(__('%s special page', 'woo-vipps'), static::CompanyName()), // we hide the title frontend in template_redirect. LP 2026-08-27
5309 + 'content' => '<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->',
5310 + ];
5163 5311 return $data;
5164 5312 }
5165 5313
5166 - // Creates any necessary Vipps pages. Will be called e.g. when activating Checkout or turning it on.
5314 + // Creates any necessary Vipps pages. E.g vipps checkout page or vipps special page. LP 2026-09-01
5315 + // If a page slug already exists, then it won't overwrite or duplicate it!. LP 2026-09-02
5167 5316 public function maybe_create_vipps_pages () {
5317 + $make_pages = false;
5318 +
5319 + // Vipps Checkout page. LP 2026-08-18
5168 5320 $checkoutid = wc_get_page_id('vipps_checkout');
5169 - $makeit = !$checkoutid || ! get_post_status($checkoutid);
5170 - if ($makeit) {
5321 + if (!$checkoutid || ! get_post_status($checkoutid)) {
5171 5322 delete_option('woocommerce_vipps_checkout_page_id');
5323 + $make_pages = true;
5172 5324 }
5173 5325
5174 - if ($makeit) {
5175 - WC_Install::create_pages();
5326 + // vipps special page, previously a fake page. LP 2026-08-18
5327 + $builtin_special_page_id = static::get_special_page_id();
5328 + if (!$builtin_special_page_id || !get_post_status($builtin_special_page_id)) {
5329 + delete_option('woocommerce_vipps_special_page_page_id');
5330 + $make_pages = true;
5176 5331 }
5332 +
5333 + if ($make_pages) {
5334 + WC_Install::create_pages();
5335 + }
5177 5336 }
5178 5337
5338 + public function vipps_special_page_shortcode($atts, $content) {
5339 + // No point in expanding this unless we are actually doing the special actions. LP 2026-08-25
5340 + if (is_admin()) return;
5341 + if (wp_doing_ajax()) return;
5342 + if (defined('REST_REQUEST') && REST_REQUEST) return;
5343 + if (did_filter('woo_vipps_special_page_html')) return; // User has somehow added two shortcodes. IOK 2026-09-18
5179 5344
5345 + $action = $_GET['action'] ?? '';
5346 + $html = "";
5347 + switch ($action) {
5348 + case 'wait_for_payment':
5349 + $html = $this->vipps_wait_for_payment();
5350 + break;
5351 + case 'do_express_checkout':
5352 + $html = $this->vipps_express_checkout();
5353 + break;
5354 + case 'buy_product':
5355 + $html = $this->vipps_buy_product();
5356 + break;
5357 + default:
5358 + $html = '';
5359 + }
5360 + // This is mostly to avoid this shortcode evaluating twice IOK 2026-09-18
5361 + $html = apply_filters('woo_vipps_special_page_html', $html, $action);
5362 +
5363 + // Remember, this is a shortcode, so the html must be returned, not echoed IOK 2026-09-11
5364 + return $html;
5365 + }
5366 +
5367 +
5180 5368 // This URL will when accessed add a product to the cart and go directly to the express checkout page.
5181 5369 // The argument passed must be a shareable link created for a given product - so this in effect acts as a landing page for
5182 5370 // the buying thru Vipps Express Checkout of a single product linked to in for instance banners. IOK 2018-09-24
5183 5371 public function vipps_buy_product() {
5184 - status_header(200,'OK');
5185 - Vipps::nocache();
5186 5372
5187 5373 add_filter('body_class', function ($classes) {
5188 5374 $classes[] = 'vipps-express-checkout';
5189 5375 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
@@ -5219,9 +5405,9 @@
5219 5405
5220 5406 if (!$productinfo) {
5221 5407 $title = __("Product is no longer available",'woo-vipps');
5222 5408 $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);
5409 + return $this->special_page_html($title,$content);
5224 5410 }
5225 5411
5226 5412 // Pass the productinfo to the express checkout form
5227 5413 $args = array();
@@ -5238,20 +5424,16 @@
5238 5424 }
5239 5425 $args[sanitize_title(wp_unslash($key))] = sanitize_text_field(wp_unslash($value));
5240 5426 }
5241 5427
5242 - $this->print_express_checkout_page(true,'do_single_product_express_checkout',$args);
5428 + return $this->express_checkout_page_html(true,'do_single_product_express_checkout',$args);
5243 5429 }
5244 5430
5245 - // 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 - public function vipps_express_checkout() {
5247 - status_header(200,'OK');
5248 - Vipps::nocache();
5431 + public function vipps_express_checkout_consistency_check() {
5249 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.
5250 5433 // IOK 2018-05-28
5251 5434 $ok = isset($_REQUEST['sec']) && wp_verify_nonce($_REQUEST['sec'],'express');
5252 5435
5253 -
5254 5436 $backurl = wp_validate_redirect(@$_SERVER['HTTP_REFERER']);
5255 5437 if (!$backurl) $backurl = home_url();
5256 5438
5257 5439 if (!$ok) {
@@ -5265,8 +5447,19 @@
5265 5447 wp_redirect($backurl);
5266 5448 exit();
5267 5449 }
5268 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 +
5269 5462 add_filter('body_class', function ($classes) {
5270 5463 $classes[] = 'vipps-express-checkout';
5271 5464 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
5272 5465 return apply_filters('woo_vipps_express_checkout_body_class', $classes);
@@ -5273,9 +5466,9 @@
5273 5466 });
5274 5467
5275 5468 do_action('woo_vipps_express_checkout_page');
5276 5469
5277 - $this->print_express_checkout_page(true, 'do_express_checkout');
5470 + return $this->express_checkout_page_html(true, 'do_express_checkout');
5278 5471 }
5279 5472
5280 5473 // This method tries to ensure that a customer does not 'lose' the return page and
5281 5474 // starts ordering the same products twice. IOK 2020-01-22
@@ -5370,9 +5563,10 @@
5370 5563 return $orderspec;
5371 5564 }
5372 5565
5373 5566 // 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) {
5567 + // Returns the html. LP 2026-08-27
5568 + protected function express_checkout_page_html($execute,$action,$productinfo=null) {
5375 5569 $gw = $this->gateway();
5376 5570
5377 5571 $expressCheckoutMessages = array();
5378 5572 $expressCheckoutMessages['termsAndConditionsError'] = __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' );
@@ -5455,10 +5649,9 @@
5455 5649
5456 5650 if ($execute) {
5457 5651 $content .= "<p id=waiting>" . __("Please wait while we are preparing your order", 'woo-vipps') . "</p>";
5458 5652 $content .= "<div id='vipps-status-message'></div>";
5459 - $this->fakepage(__('Order in progress','woo-vipps'), $content);
5460 - return;
5653 + return $this->special_page_html('', $content);
5461 5654 } else {
5462 5655 $content .= $askForConfirmationHTML;
5463 5656 $content .= $extraHTML;
5464 5657 $content .= $termsHTML;
@@ -5465,21 +5658,16 @@
5465 5658 $content .= apply_filters('woo_vipps_express_checkout_validation_elements', '');
5466 5659 $title = sprintf(__('Buy now with %1$s!', 'woo-vipps'), $this->get_payment_method_name());
5467 5660 $content .= "<div class='vipps_buy_now_wrapper noloop'><a href='#' id='do-express-checkout' class='vipps-express-checkout' title='$title'>$buttonhtml</a></div>";
5468 5661 $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;
5662 + return $this->special_page_html('', $content);
5471 5663 }
5472 5664 }
5473 5665
5474 5666
5475 -
5476 - public function vipps_wait_for_payment() {
5477 - status_header(200,'OK');
5478 - Vipps::nocache();
5479 -
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 () {
5480 5669 $orderid = WC()->session->get('_vipps_pending_order');
5481 -
5482 5670 $order = null;
5483 5671 $gw = $this->gateway();
5484 5672
5485 5673 // Failsafe for when the session disappears IOK 2018-11-19
@@ -5491,9 +5679,9 @@
5491 5679 // If so, we will read the order id from the GET arguments and check if the auth token is correct,
5492 5680 // simulating the session with that.
5493 5681 // IOK 2019-11-19, changed to using GET 2023-01-23
5494 5682 if ($no_session && $limited_session) {
5495 - $orderid = intval(@$_GET['id']);
5683 + $orderid = intval($_GET['id'] ?? false);
5496 5684 }
5497 5685 if ($orderid) {
5498 5686 clean_post_cache($orderid);
5499 5687 $order = wc_get_order($orderid);
@@ -5512,11 +5700,8 @@
5512 5700 $session->set('_vipps_pending_order', $orderid);
5513 5701 }
5514 5702 }
5515 5703
5516 -
5517 - do_action('woo_vipps_wait_for_payment_page',$order);
5518 -
5519 5704 $deleted_order=0;
5520 5705 if ($orderid && !$order) {
5521 5706 // If this happens, we actually did have an order, but it has been deleted, which must mean that it was cancelled.
5522 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
@@ -5541,9 +5726,9 @@
5541 5726 clean_post_cache($orderid);
5542 5727 $order = wc_get_order($orderid); // Reload order object
5543 5728 }
5544 5729 } else {
5545 - // No need to do anyting here. IOK 2020-01-26
5730 + // No need to do anyting here. IOK 2020-01-26
5546 5731 }
5547 5732
5548 5733 $payment = 'notchecked';
5549 5734 if ($do_poll) {
@@ -5559,9 +5744,8 @@
5559 5744 exit();
5560 5745 }
5561 5746
5562 5747 // We are done, but in failure. Don't poll.
5563 - $content = "";
5564 5748 $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid);
5565 5749
5566 5750 // Status is failed; still send to return url (as of now /order-recieved), the text there will depend on the status.
5567 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
@@ -5569,8 +5753,9 @@
5569 5753 $failure_redirect = $failure_redirect ?: $gw->get_return_url($order);
5570 5754 wp_redirect($failure_redirect);
5571 5755 exit();
5572 5756 }
5757 +
5573 5758 if ($status == 'cancelled' || $payment == 'cancelled') {
5574 5759 $this->maybe_restore_cart($orderid,'failed');
5575 5760 if ($failure_redirect){
5576 5761 wp_redirect($failure_redirect);
@@ -5575,27 +5760,45 @@
5575 5760 if ($failure_redirect){
5576 5761 wp_redirect($failure_redirect);
5577 5762 exit();
5578 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') {
5579 5793 $content .= "<div id=failure><p>". __('Order cancelled','woo-vipps') . '</p>';
5580 5794 $content .= "<p><a href='" . home_url() . "' class='btn button'>" . __('Continue shopping','woo-vipps') . '</a></p>';
5581 5795 $content .= "</div>";
5582 - $this->fakepage(__('Order cancelled','woo-vipps'), $content);
5583 -
5584 - return;
5796 + return $this->special_page_html('', $content);
5585 5797 }
5586 5798
5587 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
5588 -
5589 5800 // 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 5801 $signal = $this->callbackSignal($order);
5599 5802 $content = "";
5600 5803 $content .= "<div id='waiting'><p>" . sprintf(__('Waiting for confirmation of purchase from %1$s','woo-vipps'), $this->get_payment_method_name());
5601 5804
@@ -5603,15 +5806,16 @@
5603 5806 $signalurl = $this->callbackSignalURL($signal);
5604 5807
5605 5808 $content .= "</p></div>";
5606 5809
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) . "'>";
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'>";
5610 5814 $content .= "<input type='hidden' id='fkey' name='fkey' value='".htmlspecialchars($signalurl)."'>";
5611 5815 $content .= "<input type='hidden' name='key' value='".htmlspecialchars($order->get_order_key())."'>";
5612 5816 $content .= "<input type='hidden' name='action' value='check_order_status'>";
5613 - $content .= wp_nonce_field('vippsstatus','sec',1,false);
5817 + $content .= wp_nonce_field('vippsstatus','sec',1,false);
5614 5818 $content .= "</form>";
5615 5819
5616 5820
5617 5821 $content .= "<div id='error' style='display:none'><p>".__('Error during order confirmation','woo-vipps'). '</p>';
@@ -5628,93 +5832,21 @@
5628 5832 $content .= "<a id='continueToOrderFailed' style='display:none' href='" . $failure_redirect . "'></a>";
5629 5833 $content .= "<a id='continueToOrderFailedFallback' style='display:none' href='" . $gw->get_return_url($order) . "'></a>";
5630 5834 $content .= "</div>";
5631 5835
5836 + return $this->special_page_html('', $content);
5837 + }
5632 5838
5633 - $this->fakepage(__('Waiting for your order confirmation','woo-vipps'), $content);
5839 + // Returns formatted html for the vipps special page. LP 2026-08-27
5840 + public function special_page_html($header, $content) {
5841 + $header_html = $header ? "<h2 class='vipps-special-page-title page-title'>$header</h2>" : '';
5842 + $html = <<<EOF
5843 + $header_html
5844 + <div class="vipps-special-page-content">$content</div>
5845 + EOF;
5846 + return apply_filters('woo_vipps_special_page_html', $html, $header, $content);
5634 5847 }
5635 5848
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 5849
5718 5850 // Support the interactivity API with data about our cart IOK 2026-02-23
5719 5851 public function woo_vipps_store_api_cart_data() {
5720 5852 // 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