PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.2.1
Pay with Vipps and MobilePay for WooCommerce v6.2.1
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 5.3.4 All 184 releases
← All changes | payment/Vipps.class.php +323 -255 6.1.26.2.1 View file →
@@ -70,9 +70,9 @@
70 70 public static function CheckoutName($order=null) {
71 71 return "Vipps MobilePay Checkout"; // Do not translate
72 72 }
73 73 public static function ExpressCheckoutName($order=null) {
74 - return __("Vipps Express Checkout", 'woo-vipps');
74 + return __("Vipps MobilePay Express Checkout", 'woo-vipps');
75 75 }
76 76 public static function LoginName() {
77 77 return __("Login with Vipps", 'woo-vipps');
78 78 }
@@ -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,41 @@
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 +
319 + // Add an admin interface for this page as well IOK 2026-09-11
320 + add_action('woocommerce_settings_pages', array($this, 'woocommerce_settings_pages'));
321 +
284 322 }
285 323
286 324 public function admin_init () {
287 325 $gw = $this->gateway();
@@ -401,9 +439,77 @@
401 439 }
402 440 }
403 441 }
404 442
443 +
444 + /** Ensure we have a special page for payment flows
445 + *
446 + * woocommerce_loaded is too early for this because of maybe_create_vipps_pages which calls WC_Install::create_pages,
447 + * and we hook unto this with woocommerce_create_pages. LP 2026-09-03
448 + **/
449 + public function ensure_special_page_exists() {
450 + if (static::get_special_page_id()) return;
451 + $this->log(__('Missing id for special page, attempting to fix.', 'woo-vipps'), 'info');
405 452
453 + // 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
454 + $old_special_page_id = $this->gateway()->get_option('vippsspecialpageid');
455 + if ($old_special_page_id && ($special_page = get_post($old_special_page_id)) && "trash" !== $special_page->post_status) {
456 + $this->log(__('Migrated old special page setting.', 'woo-vipps'), 'info');
457 + // there is no wc_set_page_id() so we update the option directly. LP 2026-09-01
458 + update_option('woocommerce_vipps_special_page_page_id', $old_special_page_id);
459 +
460 + // Ensure this page has the necessary shortcode. LP 2026-09-01
461 + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) {
462 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
463 + wp_update_post([
464 + 'ID' => $old_special_page_id,
465 + 'post_content' => $new_content,
466 + ]);
467 + }
468 + } else {
469 + // Create special page if its missing. LP 2026-09-01
470 + $this->maybe_create_vipps_pages();
471 + }
472 + }
473 +
474 + // Admin interface for the special page on woo/advanced/pages
475 + public function woocommerce_settings_pages ($settings) {
476 + $i = -1;
477 + foreach($settings as $entry) {
478 + $i++;
479 + if ($entry['type'] == 'sectionend' && $entry['id'] == 'advanced_page_options') {
480 + break;
481 + }
482 + }
483 + if ($i > 0) {
484 + $vippspagesettings = array(
485 + array(
486 + 'title' => sprintf(__( '%1$s Page', 'woo-vipps' ), Vipps::CompanyName()),
487 + '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') ,
488 + 'id' => 'woocommerce_vipps_special_page_page_id',
489 + 'type' => 'single_select_page_with_search',
490 + 'default' => '',
491 + 'class' => 'wc-page-search',
492 + 'css' => 'min-width:300px;',
493 + 'args' => array(
494 + 'exclude' =>
495 + array(
496 + wc_get_page_id( 'myaccount' ),
497 + wc_get_page_id( 'checkout' ),
498 + wc_get_page_id( 'cart' ),
499 + ),
500 + ),
501 + 'desc_tip' => true,
502 + 'autoload' => false,
503 + ));
504 + array_splice($settings, $i, 0, $vippspagesettings);
505 + }
506 +
507 + return $settings;
508 + }
509 +
510 +
511 +
406 512 // Runs on init, adds the Vipps badge feature if activated
407 513 public function maybe_add_vipps_badge_feature () {
408 514 $badge_options = get_option('vipps_badge_options');
409 515 if (!$badge_options || !@$badge_options['badgeon']) return false;
@@ -732,8 +838,11 @@
732 838
733 839 // Get current brand and language
734 840 $current_brand = strtolower($this->get_payment_method_name());
735 841 $current_language = $this->get_customer_language();
842 + if ('se' === $current_language) $current_language = 'sv';
843 + // Looks like button and badge web components now use 'da' instead of 'dk' for danish. LP 2026-08-13
844 + if ('dk' === $current_language) $current_language = 'da';
736 845
737 846 $variants = ['white'=> __('White', 'woo-vipps'), 'grey' => __('Grey','woo-vipps'),
738 847 'filled'=> __('Filled', 'woo-vipps'), 'light'=>__('Light','woo-vipps'),
739 848 'purple'=> __('Purple', 'woo-vipps')];
@@ -796,9 +905,9 @@
796 905
797 906 <h2><?php _e('Shortcodes', 'woo-vipps'); ?> </h2>
798 907 <p><?php echo sprintf(__('If you need to add a %1$s badge on a specific page, footer, header and so on, and you cannot use the Gutenberg Block provided for this, you can either add the %1$s Badge manually (as <a href="%2$s" nofollow rel=nofollow target=_blank>documented here</a>) or you can use the shortcode.', 'woo-vipps'), Vipps::CompanyName(), "https://developer.vippsmobilepay.com/docs/knowledge-base/design-guidelines/on-site-messaging/"); ?></p>
799 908 <br><?php _e("The shortcode looks like this:", 'woo-vipps')?><br>
800 - <pre>[vipps-mobilepay-badge variant={white|filled|light|grey|purple}<br> language={en|no|fi|dk} ] </pre><br>
909 + <pre>[vipps-mobilepay-badge variant={white|filled|light|grey|purple}<br> language={en|no|fi|da|sv} ] </pre><br>
801 910 <?php _e("Please refer to the documentation for the meaning of the parameters.", 'woo-vipps'); ?></br>
802 911 <?php _e("The brand will be automatically applied.", 'woo-vipps'); ?>
803 912 </p>
804 913
@@ -876,9 +985,12 @@
876 985 public function vipps_mobilepay_badge_shortcode($atts) {
877 986 $args = shortcode_atts( array('id'=>'', 'class'=>'', 'brand' => '', 'variant' => '','language'=>''), $atts );
878 987
879 988 $variant = in_array($args['variant'], ['orange', 'light-orange', 'grey','white', 'purple', 'filled', 'light']) ? $args['variant'] : "";
880 - $language = in_array($args['language'], ['en','no', 'fi', 'dk']) ? $args['language'] : $this->get_customer_language();
989 + $language = in_array($args['language'], ['en', 'no', 'sv', 'da', 'dk', 'fi']) ? $args['language'] : $this->get_customer_language();
990 + if ('se' === $language) $language = 'sv';
991 + // Looks like button and badge web components now use 'da' instead of 'dk' for danish. LP 2026-08-11
992 + if ('dk' === $language) $language = 'da';
881 993 $id = sanitize_title($args['id']);
882 994 $class = sanitize_text_field($args['class']);
883 995
884 996 $attributes = [];
@@ -894,13 +1006,18 @@
894 1006 return "<vipps-mobilepay-badge $badgeatts></vipps-mobilepay-badge>";
895 1007 }
896 1008
897 1009 // legacy vipps_badge shortcode, the new one is vipps_mobilepay_badge_shortcode. LP 19.11.2024
1010 + // Diff: this one doesn't support brand (JUST VIPPS). LP 2026-08-11
898 1011 public function vipps_badge_shortcode($atts) {
899 1012 $args = shortcode_atts( array('id'=>'', 'class'=>'','variant' => '','language'=>''), $atts );
900 1013
901 1014 $variant = in_array($args['variant'], ['orange', 'light-orange', 'grey','white', 'purple']) ? $args['variant'] : "";
902 - $language = in_array($args['language'], ['en','no', 'dk', 'fi']) ? $args['language'] : $this->get_customer_language();
1015 + $language = in_array($args['language'], ['en', 'no', 'sv', 'da', 'dk', 'fi']) ? $args['language'] : $this->get_customer_language();
1016 + if ('se' === $language) $language = 'sv';
1017 + // Looks like button and badge web components now use 'da' instead of 'dk' for danish. LP 2026-08-11
1018 + if ('dk' === $language) $language = 'da';
1019 +
903 1020 $id = sanitize_title($args['id']);
904 1021 $class = sanitize_text_field($args['class']);
905 1022
906 1023 $attributes = [];
@@ -952,8 +1069,10 @@
952 1069 // Support using store language
953 1070 if ('store' === $attrs['language']) $attrs['language'] = $this->get_customer_language();
954 1071 // Don't support these login verbs. LP 2026-06-04
955 1072 if (in_array($attrs['verb'], ['login', 'register'])) $attrs['verb'] = 'buy';
1073 + // Looks like button and badge web components now use 'da' instead of 'dk' for danish. LP 2026-08-11
1074 + if ('dk' === $attrs['language']) $attrs['language'] = 'da';
956 1075
957 1076 $escaped_attrs = [];
958 1077 foreach($attrs as $k => $v) {
959 1078 $escaped_attrs[$k] = esc_attr($v);
@@ -1151,9 +1270,9 @@
1151 1270
1152 1271 // Update the preview web component's attributes. LP 2026-06-24
1153 1272 function updatePreview(event) {
1154 1273 const args = getPreviewArgs();
1155 - // 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
1274 + // 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
1156 1275 // if ('store' === args.language) args.language = '<?php echo $this->get_customer_language(); ?>';
1157 1276 if ('store' === args.language) args.language = '<?php echo substr(get_locale(), 0, 2); ?>';
1158 1277 const button = jQuery('#vipps-button-express-preview');
1159 1278 button.attr(args);
@@ -1646,9 +1765,9 @@
1646 1765 [
1647 1766 'in_footer' => true,
1648 1767 'strategy' => 'async',
1649 1768 ],
1650 - );
1769 + );
1651 1770
1652 1771 // Button web component downloaded from https://cdn.vippsmobilepay.com/js/button/button.js. LP 2026-06-24
1653 1772 wp_register_script('vipps-button-webcomponent',
1654 1773 plugins_url('js/vipps-button.js', WC_VIPPS_PAYMENT_MAIN_FILE),
@@ -1654,10 +1773,9 @@
1654 1773 plugins_url('js/vipps-button.js', WC_VIPPS_PAYMENT_MAIN_FILE),
1655 1774 array(),
1656 1775 filemtime(dirname(WC_VIPPS_PAYMENT_MAIN_FILE) . '/js/vipps-button.js'),
1657 1776 [
1658 - 'in_footer' => true,
1659 - 'strategy' => 'async',
1777 + 'in_footer' => false
1660 1778 ],
1661 1779 );
1662 1780 }
1663 1781
@@ -1692,8 +1810,11 @@
1692 1810 // New vipps-mobilepay-badge shortcode. LP 19.11.2024
1693 1811 add_shortcode('vipps-mobilepay-badge', array($this, 'vipps_mobilepay_badge_shortcode'));
1694 1812 // Legacy vipps-badge shortcode. LP 19.11.2024
1695 1813 add_shortcode('vipps-badge', array($this, 'vipps_badge_shortcode'));
1814 +
1815 + // special page handling, previously a fake page. LP 2026-08-25
1816 + add_shortcode('vipps_special_page', array($this, 'vipps_special_page_shortcode'));
1696 1817 }
1697 1818
1698 1819
1699 1820 public function log ($what,$type='info') {
@@ -1719,8 +1840,10 @@
1719 1840 }
1720 1841
1721 1842 // Show express button option on checkout form. LP 2026-03-23
1722 1843 public function checkout_before_customer_details_express () {
1844 + if (did_action('woo_vipps_checkout_before_customer_details_express')) return;
1845 + do_action('woo_vipps_checkout_before_customer_details_express');
1723 1846 $gw = $this->gateway();
1724 1847 if (!$gw->show_express_checkout()) return;
1725 1848 $this->express_checkout_section_html();
1726 1849 }
@@ -2506,77 +2629,69 @@
2506 2629 return null;
2507 2630 }
2508 2631 }
2509 2632
2633 + // Special pages, and some callbacks. IOK 2018-05-18
2634 + public function template_redirect() {
2510 2635
2511 - // If this is a special page, return true very early because we are handling this. IOK 2023-02-22
2512 - public function pre_handle_404($current, $query) {
2513 - if (!is_admin()) {
2514 - $special = $this->is_special_page();
2515 - if ($special) {
2516 - // Ensure very early on that Autooptimize does not try to optimize us (if installed) IOK 2023-03-04
2517 - add_filter( 'autoptimize_filter_noptimize', '__return_true');
2518 - return true;
2519 - }
2636 + // Handle legacy vipps-buy-now urls that auto-start express checkout for certain product - in QR codes etc IOK 2026-09-11
2637 + // We redirect these to the new location.
2638 + $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
2639 + if (( ($_GET['VippsSpecialPage'] ?? '') == 'vipps-buy-product') || ($path && preg_match("!/vipps-buy-product/?$!", $path)) ) {
2640 + $url = static::get_special_page_url();
2641 + $_GET['action'] = 'buy_product';
2642 + $q = build_query($_GET);
2643 + wp_redirect($url . "?" . $q, 302);
2644 + exit();
2520 2645 }
2521 - return $current;
2522 - }
2523 2646
2524 - // Special pages, and some callbacks. IOK 2018-05-18
2525 - public function template_redirect() {
2526 - global $post;
2527 - // Handle special callbacks
2528 - $special = $this->is_special_page() ;
2529 -
2530 - if ($special) {
2647 + if (static::is_special_page()) {
2648 + // Legacy: Stop the canonical redirect here. Unclear if still necessary. IOK 2026-09-11
2531 2649 remove_filter('template_redirect', 'redirect_canonical', 10);
2532 - do_action('woo_vipps_before_handling_special_page', $special);
2650 + // dont cache special page. LP 2026-08-25
2651 + $this->nocache();
2652 + // Do the custom pre-load actions for these pages IOK 2026-09-11
2653 + do_action('woo_vipps_before_handling_special_page', $_GET['action']);
2654 + }
2655 + }
2533 2656
2534 - // Allow above hook to actually handle special pages. It should probably call $Vipps->fakepage or a redirect; can be used
2535 - // to intercept express checkout etc. IOK 2022-03-18
2536 - if (! apply_filters('woo_vipps_special_page_handled', false, $special)) {
2537 - $this->$special();
2538 - }
2539 - }
2657 + // Dynamic special page title depending on endpoint/action, only frontend. LP 2026-09-02
2658 + public function vipps_special_page_endpoint_title($title, $postid = 0) {
2659 + global $wp_query;
2660 + // Comment from woocommerce's wc_page_endpoint_title where this logic is from: LP 2026-09-02
2540 2661
2541 - $consentremoval = $this->is_consent_removal();
2542 - if ($consentremoval) {
2543 - remove_filter('template_redirect', 'redirect_canonical', 10);
2544 - do_action('woo_vipps_before_handling_special_page', 'consentremoval');
2545 - if (! apply_filters('woo_vipps_special_page_handled', false, 'consentremoval')) {
2546 - $this->vipps_consent_removal_callback($consentremoval);
2662 + // In block themes the whole template (header, footer, content) renders inside the main
2663 + // loop, so `the_title` fires for any post title rendered on the page (e.g. a product in a
2664 + // server-rendered mini-cart) - not just the page's own heading. Only replace the title of
2665 + // the queried page so an earlier title doesn't consume this one-shot filter.
2666 + if ( ! is_null( $wp_query ) && ! is_admin() && is_main_query() && in_the_loop() && is_page() && $postid == static::get_special_page_id() ) {
2667 + switch ($_GET['action'] ?? '') {
2668 + case 'wait_for_payment':
2669 + $title = __('Processing order', 'woo-vipps');
2670 + break;
2671 + case 'do_express_checkout':
2672 + case 'buy_product':
2673 + $title = __('Express Checkout', 'woo-vipps');
2674 + break;
2547 2675 }
2548 2676 }
2677 + return $title;
2549 2678 }
2679 +
2550 2680 // Template handling for special pages. IOK 2018-11-21
2681 + // 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
2551 2682 public function template_include($template) {
2552 - $special = $this->is_special_page() ;
2553 - if ($special) {
2683 + if (static::is_special_page()) {
2554 2684 // Get any special template override from the options IOK 2020-02-18
2555 2685 $specific = $this->gateway()->get_option('vippsspecialpagetemplate');
2556 2686 $found = locate_template($specific,false,false);
2557 2687 if ($found) $template=$found;
2558 2688
2559 - return apply_filters('woo_vipps_special_page_template', $template, $special);
2689 + return apply_filters('woo_vipps_special_page_template', $template, $_GET['action'] ?? '');
2560 2690 }
2561 2691 return $template;
2562 2692 }
2563 2693
2564 -
2565 - // Can't use wc-api for this, as that does not support DELETE . IOK 2018-05-18
2566 - private function is_consent_removal () {
2567 -
2568 - if ($_SERVER['REQUEST_METHOD'] != 'DELETE') return false;
2569 - if ( !get_option('permalink_structure')) {
2570 - if (@$_REQUEST['vipps-consent-removal']) return @$_REQUEST['callback'];
2571 - return false;
2572 - }
2573 - if (preg_match("!/vipps-consent-removal/([^/]*)!", $_SERVER['REQUEST_URI'], $matches)) {
2574 - return @$_REQUEST['callback'];
2575 - }
2576 - return false;
2577 - }
2578 -
2579 2694 // On the thank you page, we have a completed order, so we need to restore any saved cart and possibly log in
2580 2695 // the user if using Express Checkout IOK 2020-10-09
2581 2696 public function woocommerce_before_thankyou ($orderid) {
2582 2697 $order = wc_get_order($orderid);
@@ -2581,9 +2696,9 @@
2581 2696 public function woocommerce_before_thankyou ($orderid) {
2582 2697 $order = wc_get_order($orderid);
2583 2698 if ($order) {
2584 2699 // Requires that this is express checkout and that 'create users on express checkout' is chosen. IOK 2020-10-09
2585 - // -- 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
2700 + // -- or the same thing for Checkout. Also, the NHG code should not be running, and there is a filter, too. IOK 2023-08-04
2586 2701 $this->maybe_log_in_user($order);
2587 2702 $order->delete_meta_data('_vipps_limited_session');
2588 2703 $order->save();
2589 2704
@@ -2644,9 +2759,8 @@
2644 2759
2645 2760 // Support adding pickup locations to any shipping rate using the 'woo_vipps_shipping_method_pickup_points' filter
2646 2761 // IOK 2025-11-19
2647 2762 add_filter('woo_vipps_modify_express_checkout_rate', array($this, 'express_add_pickup_location_options'), 10, 4);
2648 -
2649 2763 }
2650 2764
2651 2765 public function get_payment_method_name() {
2652 2766 return $this->gateway()->get_option('payment_method_name');
@@ -2666,14 +2780,13 @@
2666 2780 public function after_setup_theme() {
2667 2781 // To facilitate development, allow loading the plugin-supplied translations. Must be called here at the earliest.
2668 2782 $ok = Vipps::load_plugin_textdomain('woo-vipps', false, basename( dirname( dirname( __FILE__ ) ) ) . "/languages");
2669 2783
2670 - // Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
2784 + // Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
2671 2785 // Will also probably be used to maintain a real utility-page for Vipps actions later for themes where this
2672 2786 // is important.
2673 2787 add_filter('woocommerce_create_pages', array($this, 'woocommerce_create_pages'), 50, 1);
2674 2788
2675 -
2676 2789 // Callbacks use the Woo API IOK 2018-05-18
2677 2790 add_action( 'woocommerce_api_wc_gateway_vipps', array($this,'vipps_callback'));
2678 2791 add_action( 'woocommerce_api_vipps_shipping_details', array($this,'vipps_shipping_details_callback'));
2679 2792
@@ -2684,9 +2797,9 @@
2684 2797 add_action( 'woocommerce_cart_actions', array($this, 'cart_express_checkout_button'));
2685 2798 add_action( 'woocommerce_widget_shopping_cart_buttons', array($this, 'minicart_express_checkout_button'), 30);
2686 2799
2687 2800 // Previously we added an express html banner to the action 'woocommerce_before_checkout_form.',
2688 - // replaced by the new express buttons in manner more like Gutenberg. LP 2026-03-23
2801 + // replaced by the new express buttons in manner more like Gutenberg. for grepping: "express legacy checkout". LP 2026-03-23
2689 2802 add_action('woocommerce_checkout_before_customer_details', array($this, 'checkout_before_customer_details_express'), 5);
2690 2803
2691 2804 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2692 2805 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
@@ -2691,12 +2804,10 @@
2691 2804 add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button'));
2692 2805 add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20);
2693 2806
2694 2807
2695 - // Special pages and callbacks handled by template_redirect
2696 - // We must also notify WP and other plugins that we are handling this 404-like situation. IOK 2023-02-22
2808 + // Special pages and callbacks handled by template_redirect. IOK 2023-02-22
2697 2809 add_action('template_redirect', array($this,'template_redirect'),1);
2698 - add_action('pre_handle_404', array($this, 'pre_handle_404'), 1, 2);
2699 2810
2700 2811 // Allow overriding their templates
2701 2812 add_filter('template_include', array($this,'template_include'), 10, 1);
2702 2813
@@ -2982,14 +3093,14 @@
2982 3093
2983 3094 $raw_post = @file_get_contents( 'php://input' );
2984 3095 $result = @json_decode($raw_post,true);
2985 3096
2986 - // This handler handles both Vipps Checkout and Vipps ECom IOK 2021-09-02
3097 + // This handler handles both Checkout and Vipps ECom IOK 2021-09-02
2987 3098 // .. and the epayment webhooks 2023-12-19
2988 3099 $ischeckout = false;
2989 3100 $iswebhook = false;
2990 3101 $callback = isset($_REQUEST['callback']) ? $_REQUEST['callback'] : "";
2991 - // For Vipps Checkout v3 and onwards, we control the callback so the type is just this field
3102 + // For Checkout v3 and onwards, we control the callback so the type is just this field
2992 3103 if ($callback == 'checkout') {
2993 3104 $ischeckout = true;
2994 3105 }
2995 3106 // For the webhooks, we will add 'webhook' to the result, but we also know that 'pspReference' will be present. IOK 2023-12-19
@@ -3403,10 +3514,10 @@
3403 3514 $this->log(sprintf(__("Wrong %1\$s Orderid on shipping details callback", 'woo-vipps'), $this->get_payment_method_name()), 'warning');
3404 3515 exit();
3405 3516 }
3406 3517
3407 - // If we are doing this for Vipps Checkout after version 3, communicate to any shipping methods with
3408 - // special support for Vipps Checkout that this is in fact happening. IOK 2023-01-19
3518 + // If we are doing this for Checkout after version 3, communicate to any shipping methods with
3519 + // special support for Checkout that this is in fact happening. IOK 2023-01-19
3409 3520 // This needs to be done before "calculate totals".
3410 3521 // Moved from "vipps_shipping_details_callback_handler" because we need it before restoring sessions. IOK 2025-05-06
3411 3522 $ischeckout = $order->get_meta('_vipps_checkout');
3412 3523
@@ -3582,9 +3693,9 @@
3582 3693 ), 'debug');
3583 3694
3584 3695 }
3585 3696
3586 - // Add shipping tax rates to the *order* so we can calculate this correctly when using Vipps Checkouts
3697 + // Add shipping tax rates to the *order* so we can calculate this correctly when using Checkouts
3587 3698 // 'dynamic pricing' 2023-01-26
3588 3699 // Which may be deprecated, but anyway, for future use IOK 2025-08-14
3589 3700 $taxrate = 0;
3590 3701 if (is_array($shipping_tax_rates) && !empty($shipping_tax_rates)) {
@@ -3710,9 +3821,9 @@
3710 3821 $vippsmethod['shippingMethod'] = $rate->get_label();
3711 3822 $vippsmethod['shippingMethodId'] = $key;
3712 3823 $vippsmethods[]=$vippsmethod;
3713 3824
3714 - // Metadata and settings stored for later use for Vipps Checkout
3825 + // Metadata and settings stored for later use for Checkout
3715 3826 // and express checkout - basically, for each *key* have the corresponding object. IOK 2025-08-15
3716 3827 // In the end, this data will be serialized and stored in the Order, and used in the gateways method set_order_shipping_details to
3717 3828 // finalize the order. IOK 2025-08-15
3718 3829 $ratemap[$key]=$rate;
@@ -3730,9 +3841,9 @@
3730 3841 // This then is the old Express Checkout format, which we have exposed in filters. IOK 2025-08-14
3731 3842 $return = array('addressId'=>intval($addressid), 'orderId'=>$vippsorderid, 'shippingDetails'=>$vippsmethods);
3732 3843 $return = apply_filters('woo_vipps_vipps_formatted_shipping_methods', $return); // Mostly for debugging
3733 3844
3734 - // IOK 2021-11-16 Vipps Checkout uses a slightly different syntax and format.
3845 + // IOK 2021-11-16 Checkout uses a slightly different syntax and format.
3735 3846 // IOK 2025-08-15 and new Express yet another slightly different format.
3736 3847 // IOK 2025-08-15 pass the ratemap as a reference, so transforms can update them
3737 3848 if ($ischeckout) {
3738 3849 $return = VippsCheckout::instance()->format_shipping_methods($return, $ratemap, $methodmap, $order);
@@ -4108,17 +4219,8 @@
4108 4219 header("X-Accel-Expires: 0");
4109 4220 }
4110 4221
4111 4222
4112 -
4113 - // Handle DELETE on a vipps consent removal callback
4114 - public function vipps_consent_removal_callback ($callback) {
4115 - Vipps::nocache();
4116 - // Currently, no such requests will be posted, and as this code isn't sufficiently tested,we'll just have
4117 - // to escape here when the API is changed. IOK 2020-10-14
4118 - $this->log("Consent removal is non-functional pending API changes as of 2020-10-14"); print "1"; exit();
4119 - }
4120 -
4121 4223 public function woocommerce_payment_gateways($methods) {
4122 4224 require_once(dirname(__FILE__) . "/WC_Gateway_Vipps.class.php");
4123 4225 require_once(dirname(__FILE__) . "/WC_Gateway_VippsCard.class.php");
4124 4226 // Protect the singleton: Use the object instead of the class name IOK 2025-02-04
@@ -4212,9 +4314,9 @@
4212 4314 // Poll status and correct woo status. LP 2026-05-19
4213 4315 $order_data = $gw->get_payment_details($order);
4214 4316
4215 4317 // If we already know the order failed, we don't need to process the order further below. LP 2026-05-19
4216 - if ('CANCEL' === $order_data['STATE']) {
4318 + if ('CANCEL' === ($order_data['state'] ?? "")) {
4217 4319 /* translators: company name */
4218 4320 $order->update_status('cancelled', sprintf(__('Payment cancelled at %1$s.', 'woo-vipps'), Vipps::CompanyName()));
4219 4321 return;
4220 4322 }
@@ -4241,20 +4343,40 @@
4241 4343 }
4242 4344 }
4243 4345
4244 4346 public function activate () {
4245 - static::maybe_add_cron_event();
4246 - $gw = $this->gateway();
4347 + static::maybe_add_cron_event();
4348 + $gw = $this->gateway();
4247 4349
4248 - // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19
4249 - if ($gw->get_option('orderprefix') == 'Woo') {
4250 - $gw->update_option('orderprefix', $this->generate_order_prefix());
4251 - }
4252 - // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point.
4253 - $gw->initialize_webhooks();
4254 - $this->payment_method_name = $gw->get_option('payment_method_name');
4255 - }
4350 + // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19
4351 + if ($gw->get_option('orderprefix') == 'Woo') {
4352 + $gw->update_option('orderprefix', $this->generate_order_prefix());
4353 + }
4354 + // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point.
4355 + $gw->initialize_webhooks();
4356 + $this->payment_method_name = $gw->get_option('payment_method_name');
4256 4357
4358 +
4359 + // Check if the special page is noted and actually does exist
4360 + $special = static::get_special_page_id();
4361 + if ($special) {
4362 + $special_page = get_post($special);
4363 + if ($special_page && 'trash' !== $special_page->post_status) {
4364 + // Ensure this page has the necessary shortcode. LP 2026-09-01
4365 + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) {
4366 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
4367 + wp_update_post([
4368 + 'ID' => $special,
4369 + 'post_content' => $new_content,
4370 + ]);
4371 + }
4372 + } else {
4373 + delete_option('woocommerce_vipps_special_page_page_id');
4374 + }
4375 + }
4376 +
4377 + }
4378 +
4257 4379 // We have added some hooks to wp-cron; remove these. IOK 2020-04-01
4258 4380 public static function deactivate() {
4259 4381 $timestamp = wp_next_scheduled('vipps_cron_cleanup_hook');
4260 4382 wp_unschedule_event($timestamp, 'vipps_cron_cleanup_hook');
@@ -4307,9 +4429,10 @@
4307 4429 // If setting is true, use Vipps as default payment. Called by the woocommrece_cart_updated hook. IOK 2018-06-06
4308 4430 private function maybe_set_vipps_as_default() {
4309 4431 if (WC()->session->get('chosen_payment_method')) return; // User has already chosen payment method, so we're done.
4310 4432 $gw = $this->gateway();
4311 - if ($gw->get_option('vippsdefault')=='yes') {
4433 + // Do *not* default to vipps if Kustom Checkout is installed IOK 2026-09-11
4434 + if ($gw->get_option('vippsdefault')=='yes' && !class_exists('KCO')) {
4312 4435 WC()->session->set('chosen_payment_method', $gw->id);
4313 4436 }
4314 4437 }
4315 4438
@@ -4414,13 +4537,13 @@
4414 4537 if (is_user_logged_in()) return;
4415 4538 if (!$order || ! self::is_vipps_order($order)) return;
4416 4539
4417 4540 // We *do* want to log in express checkout customers, but not those that
4418 - // use the Vipps Checkout solution - those can change their emails in the
4541 + // use the Checkout solution - those can change their emails in the
4419 4542 // checkout screen. IOK 2021-09-03
4420 4543 $do_login = $order->get_meta('_vipps_express_checkout');
4421 4544
4422 - // We will not log in Vipps Checkout users unless the option for that is true
4545 + // We will not log in Checkout users unless the option for that is true
4423 4546 if ($order->get_meta('_vipps_checkout') && 'yes' != $this->gateway()->get_option('checkoutcreateuser')) {
4424 4547 $do_login = false;
4425 4548 }
4426 4549
@@ -4455,9 +4578,9 @@
4455 4578
4456 4579 // Both Checkout and Express Checkout have the below value set to true
4457 4580 if (!$order->get_meta('_vipps_express_checkout')) return;
4458 4581
4459 - // Creating/logging in users are handled separately for Vipps Checkout and Express Checkout, so check the correct setting
4582 + // Creating/logging in users are handled separately for Checkout and Express Checkout, so check the correct setting
4460 4583 // IOK 2023-07-27
4461 4584 $ischeckout = $order->get_meta('_vipps_checkout');
4462 4585 if ($ischeckout) {
4463 4586 if ($this->gateway()->get_option('checkoutcreateuser') != 'yes') return null;
@@ -4842,9 +4965,9 @@
4842 4965 $ok = wc()->shipping->register_shipping_method( new Automattic\WooCommerce\Blocks\Shipping\PickupLocation() );
4843 4966 }
4844 4967 }
4845 4968
4846 - // Vipps Checkout and Express Checkout allows loading specific kinds of shipping methods with non-standard APIs, such as PickupLocations. IOK 2025-05-08
4969 + // Checkout and Express Checkout allows loading specific kinds of shipping methods with non-standard APIs, such as PickupLocations. IOK 2025-05-08
4847 4970 // Must be called *early*. IOK 2025-05-08. Called in callback methods, and if using static shipping, in the 'start session' callback.
4848 4971 public function load_extra_shipping_methods($order, $addressdata, $ischeckout=false) {
4849 4972 // 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
4850 4973 add_action('woocommerce_load_shipping_methods', function () use ($order, $addressdata) {
@@ -4915,46 +5038,37 @@
4915 5038 wp_send_json(array('status'=>'error', 'msg'=> __('Unknown payment status','woo-vipps') . ' ' . $payment));
4916 5039 return false;
4917 5040 }
4918 5041
4919 - // The various return URLs for special pages of the Vipps stuff depend on settings and pretty-URLs so we supply them from here
4920 - // These are for the "fallback URL" mostly. IOK 2018-05-18
4921 - private function make_vipps_url($what) {
4922 - if ( !get_option('permalink_structure')) {
4923 - return add_query_arg('VippsSpecialPage', $what, home_url("/", 'https'));
4924 - }
4925 - return trailingslashit(home_url($what, 'https'));
5042 + // 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
5043 + private function make_special_page_url($action) {
5044 + return add_query_arg('action', $action, $this->get_special_page_url());
4926 5045 }
5046 +
4927 5047 public function payment_return_url() {
4928 - return apply_filters('woo_vipps_payment_return_url', $this->make_vipps_url('vipps-betaling'));
5048 + return apply_filters('woo_vipps_payment_return_url', $this->make_special_page_url('wait_for_payment'));
4929 5049 }
4930 5050 public function express_checkout_url() {
4931 - return $this->make_vipps_url('vipps-express-checkout');
5051 + return $this->make_special_page_url('do_express_checkout');
4932 5052 }
4933 5053 public function buy_product_url() {
4934 - return $this->make_vipps_url('vipps-buy-product');
5054 + return $this->make_special_page_url('buy_product');
4935 5055 }
4936 5056
4937 - // Return the method in the Vipps
4938 - public function is_special_page() {
4939 - $specials = array('vipps-betaling' => 'vipps_wait_for_payment', 'vipps-express-checkout'=>'vipps_express_checkout', 'vipps-buy-product'=>'vipps_buy_product');
4940 - $method = null;
4941 - if ( get_option('permalink_structure')) {
4942 - foreach($specials as $special=>$specialmethod) {
4943 - // IOK 2018-06-07 Change to add any prefix from home-url for better matching IOK 2018-06-07
4944 - $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
4945 - if ($path && preg_match("!/$special/?$!", $path, $matches)) {
4946 - $method = $specialmethod; break;
4947 - }
4948 - }
4949 - } else {
4950 - if (isset($_GET['VippsSpecialPage'])) {
4951 - $method = @$specials[$_GET['VippsSpecialPage']];
4952 - }
4953 - }
4954 - return $method;
5057 + public static function is_special_page() {
5058 + $id = static::get_special_page_id();
5059 + return $id && is_page($id);
4955 5060 }
4956 5061
5062 + public static function get_special_page_id() {
5063 + $id = wc_get_page_id('vipps_special_page'); // -1 if not found
5064 + return $id > 0 ? $id : null;
5065 + }
5066 +
5067 + public static function get_special_page_url() {
5068 + return get_permalink(static::get_special_page_id());
5069 + }
5070 +
4957 5071 // Just create a spinner and a overlay.
4958 5072 public function spinner () {
4959 5073 $flavour = sanitize_title($this->get_payment_method_name());
4960 5074 ob_start();
@@ -5001,16 +5115,8 @@
5001 5115 }
5002 5116 return null;
5003 5117 }
5004 5118
5005 - // DEPRECATED: Legacy function as of new web component express buttons. see get_buy_now_button and get_html_button. LP 2026-06-26
5006 - public function get_buy_now_button_manual($product_id, $variation_id=null, $sku=null, $disabled=false, $classes='',
5007 - $_logo_variant=null, $_logo_lang=null, // deprecated params
5008 - $context='global', $button_args_override = [],
5009 - ) {
5010 - return $this->get_buy_now_button($product_id, $variation_id, $sku, $disabled, $classes, $context, $button_args_override);
5011 - }
5012 -
5013 5119 // Code that will generate various versions of the 'buy now with Vipps' button IOK 2018-09-27
5014 5120 // $context is slug describing where its to be used, like 'catalog', 'cart', 'product' etc. and will
5015 5121 // be used unless $button_args_override is nonempty. See init_button_options() and get_html_button() LP 2026-06-26
5016 5122 public function get_buy_now_button($product_id,$variation_id=null,$sku=null,$disabled=false, $classes='', $context='global', $button_args_override = []) {
@@ -5029,9 +5135,8 @@
5029 5135 if ($sku) $data['product_sku'] = $sku;
5030 5136 if ($product_id) $data['product_id'] = $product_id;
5031 5137 if ($variation_id) $data['variation_id'] = $variation_id;
5032 5138
5033 -
5034 5139 $buttoncode = "<a href='javascript:void(0)' $disabled ";
5035 5140 foreach($data as $key=>$value) {
5036 5141 $value = esc_attr($value);
5037 5142 $buttoncode .= " data-$key='$value' ";
@@ -5055,8 +5160,11 @@
5055 5160 if ($classes) $classes = " $classes";
5056 5161 if ($short) $classes = "short $classes";
5057 5162
5058 5163 $buttoncode .= " class='single-product button vipps-buy-now $payment_method $disabled$classes' title='$title'>$button</a>";
5164 +
5165 +
5166 +
5059 5167 return apply_filters('woo_vipps_buy_now_button', $buttoncode, $product_id, $variation_id, $sku, $disabled);
5060 5168 }
5061 5169
5062 5170 // Display a 'buy now with express checkout' button on the product page IOK 2018-09-27
@@ -5140,43 +5248,90 @@
5140 5248 }
5141 5249
5142 5250
5143 5251
5144 - // Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
5252 + // Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist
5145 5253 // IOK 2026-04-30 remove this when checkout is end-of-life'd
5254 + // We now also use this for the vipps special page, previously a fakepage. LP 2026-08-18
5146 5255 public function woocommerce_create_pages ($data) {
5256 + // Vipps Checkout page
5147 5257 $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false);
5148 - if (!$vipps_checkout_activated) return $data;
5258 + if ($vipps_checkout_activated) {
5259 + $data['vipps_checkout'] = array(
5260 + 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ),
5261 + 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ),
5262 + 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->',
5263 + );
5264 + }
5149 5265
5150 - $data['vipps_checkout'] = array(
5151 - 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ),
5152 - 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ),
5153 - 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->',
5154 - );
5155 -
5266 + // Vipps special page for certain payment flow actions. Previously a fake page. LP 2026-08-18
5267 + $data['vipps_special_page'] = [
5268 + 'name' => 'vipps-payment', // slug
5269 + /* translators: company name */
5270 + 'title' => sprintf(__('%s special page', 'woo-vipps'), static::CompanyName()), // we hide the title frontend in template_redirect. LP 2026-08-27
5271 + 'content' => '<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->',
5272 + ];
5156 5273 return $data;
5157 5274 }
5158 5275
5159 - // Creates any necessary Vipps pages. Will be called e.g. when activating Vipps Checkout or turning it on.
5276 + // Creates any necessary Vipps pages. E.g vipps checkout page or vipps special page. LP 2026-09-01
5277 + // If a page slug already exists, then it won't overwrite or duplicate it!. LP 2026-09-02
5160 5278 public function maybe_create_vipps_pages () {
5279 + $make_pages = false;
5280 +
5281 + // Vipps Checkout page. LP 2026-08-18
5161 5282 $checkoutid = wc_get_page_id('vipps_checkout');
5162 - $makeit = !$checkoutid || ! get_post_status($checkoutid);
5163 - if ($makeit) {
5283 + if (!$checkoutid || ! get_post_status($checkoutid)) {
5164 5284 delete_option('woocommerce_vipps_checkout_page_id');
5285 + $make_pages = true;
5165 5286 }
5166 5287
5167 - if ($makeit) {
5168 - WC_Install::create_pages();
5288 + // vipps special page, previously a fake page. LP 2026-08-18
5289 + $builtin_special_page_id = static::get_special_page_id();
5290 + if (!$builtin_special_page_id || !get_post_status($builtin_special_page_id)) {
5291 + delete_option('woocommerce_vipps_special_page_page_id');
5292 + $make_pages = true;
5169 5293 }
5294 +
5295 + if ($make_pages) {
5296 + WC_Install::create_pages();
5297 + }
5170 5298 }
5171 5299
5300 + public function vipps_special_page_shortcode($atts, $content) {
5301 + // No point in expanding this unless we are actually doing the special actions. LP 2026-08-25
5302 + if (is_admin()) return;
5303 + if (wp_doing_ajax()) return;
5304 + if (defined('REST_REQUEST') && REST_REQUEST) return;
5305 + if (did_filter('woo_vipps_special_page_html')) return; // User has somehow added two shortcodes. IOK 2026-09-18
5172 5306
5307 + $action = $_GET['action'] ?? '';
5308 + $html = "";
5309 + switch ($action) {
5310 + case 'wait_for_payment':
5311 + $html = $this->vipps_wait_for_payment();
5312 + break;
5313 + case 'do_express_checkout':
5314 + $html = $this->vipps_express_checkout();
5315 + break;
5316 + case 'buy_product':
5317 + $html = $this->vipps_buy_product();
5318 + break;
5319 + default:
5320 + $html = '';
5321 + }
5322 + // This is mostly to avoid this shortcode evaluating twice IOK 2026-09-18
5323 + $html = apply_filters('woo_vipps_special_page_html', $html, $action);
5324 +
5325 + // Remember, this is a shortcode, so the html must be returned, not echoed IOK 2026-09-11
5326 + return $html;
5327 + }
5328 +
5329 +
5173 5330 // This URL will when accessed add a product to the cart and go directly to the express checkout page.
5174 5331 // The argument passed must be a shareable link created for a given product - so this in effect acts as a landing page for
5175 5332 // the buying thru Vipps Express Checkout of a single product linked to in for instance banners. IOK 2018-09-24
5176 5333 public function vipps_buy_product() {
5177 - status_header(200,'OK');
5178 - Vipps::nocache();
5179 5334
5180 5335 add_filter('body_class', function ($classes) {
5181 5336 $classes[] = 'vipps-express-checkout';
5182 5337 $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24
@@ -5212,9 +5367,9 @@
5212 5367
5213 5368 if (!$productinfo) {
5214 5369 $title = __("Product is no longer available",'woo-vipps');
5215 5370 $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');
5216 - return $this->fakepage($title,$content);
5371 + return $this->special_page_html($title,$content);
5217 5372 }
5218 5373
5219 5374 // Pass the productinfo to the express checkout form
5220 5375 $args = array();
@@ -5231,15 +5386,13 @@
5231 5386 }
5232 5387 $args[sanitize_title(wp_unslash($key))] = sanitize_text_field(wp_unslash($value));
5233 5388 }
5234 5389
5235 - $this->print_express_checkout_page(true,'do_single_product_express_checkout',$args);
5390 + return $this->express_checkout_page_html(true,'do_single_product_express_checkout',$args);
5236 5391 }
5237 5392
5238 5393 // This is a landing page for the express checkout of then normal cart - it is done like this because this could take time on slower hosts.
5239 5394 public function vipps_express_checkout() {
5240 - status_header(200,'OK');
5241 - Vipps::nocache();
5242 5395 // We need a nonce to get here, but we should only get here when we have a cart, so this will not be cached.
5243 5396 // IOK 2018-05-28
5244 5397 $ok = isset($_REQUEST['sec']) && wp_verify_nonce($_REQUEST['sec'],'express');
5245 5398
@@ -5266,9 +5419,9 @@
5266 5419 });
5267 5420
5268 5421 do_action('woo_vipps_express_checkout_page');
5269 5422
5270 - $this->print_express_checkout_page(true, 'do_express_checkout');
5423 + return $this->express_checkout_page_html(true, 'do_express_checkout');
5271 5424 }
5272 5425
5273 5426 // This method tries to ensure that a customer does not 'lose' the return page and
5274 5427 // starts ordering the same products twice. IOK 2020-01-22
@@ -5363,9 +5516,10 @@
5363 5516 return $orderspec;
5364 5517 }
5365 5518
5366 5519 // Used as a landing page for launching express checkout - borh for the cart and for single products. IOK 2018-09-28
5367 - protected function print_express_checkout_page($execute,$action,$productinfo=null) {
5520 + // Returns the html. LP 2026-08-27
5521 + protected function express_checkout_page_html($execute,$action,$productinfo=null) {
5368 5522 $gw = $this->gateway();
5369 5523
5370 5524 $expressCheckoutMessages = array();
5371 5525 $expressCheckoutMessages['termsAndConditionsError'] = __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' );
@@ -5448,10 +5602,9 @@
5448 5602
5449 5603 if ($execute) {
5450 5604 $content .= "<p id=waiting>" . __("Please wait while we are preparing your order", 'woo-vipps') . "</p>";
5451 5605 $content .= "<div id='vipps-status-message'></div>";
5452 - $this->fakepage(__('Order in progress','woo-vipps'), $content);
5453 - return;
5606 + return $this->special_page_html('', $content);
5454 5607 } else {
5455 5608 $content .= $askForConfirmationHTML;
5456 5609 $content .= $extraHTML;
5457 5610 $content .= $termsHTML;
@@ -5458,10 +5611,9 @@
5458 5611 $content .= apply_filters('woo_vipps_express_checkout_validation_elements', '');
5459 5612 $title = sprintf(__('Buy now with %1$s!', 'woo-vipps'), $this->get_payment_method_name());
5460 5613 $content .= "<div class='vipps_buy_now_wrapper noloop'><a href='#' id='do-express-checkout' class='vipps-express-checkout' title='$title'>$buttonhtml</a></div>";
5461 5614 $content .= "<div id='vipps-status-message'></div>";
5462 - $this->fakepage(sprintf(__('%1$s Express Checkout','woo-vipps'), $this->get_payment_method_name()), $content);
5463 - return;
5615 + return $this->special_page_html('', $content);
5464 5616 }
5465 5617 }
5466 5618
5467 5619
@@ -5466,11 +5618,8 @@
5466 5618
5467 5619
5468 5620
5469 5621 public function vipps_wait_for_payment() {
5470 - status_header(200,'OK');
5471 - Vipps::nocache();
5472 -
5473 5622 $orderid = WC()->session->get('_vipps_pending_order');
5474 5623
5475 5624 $order = null;
5476 5625 $gw = $this->gateway();
@@ -5505,9 +5654,8 @@
5505 5654 $session->set('_vipps_pending_order', $orderid);
5506 5655 }
5507 5656 }
5508 5657
5509 -
5510 5658 do_action('woo_vipps_wait_for_payment_page',$order);
5511 5659
5512 5660 $deleted_order=0;
5513 5661 if ($orderid && !$order) {
@@ -5571,11 +5719,9 @@
5571 5719 }
5572 5720 $content .= "<div id=failure><p>". __('Order cancelled','woo-vipps') . '</p>';
5573 5721 $content .= "<p><a href='" . home_url() . "' class='btn button'>" . __('Continue shopping','woo-vipps') . '</a></p>';
5574 5722 $content .= "</div>";
5575 - $this->fakepage(__('Order cancelled','woo-vipps'), $content);
5576 -
5577 - return;
5723 + return $this->special_page_html('', $content);
5578 5724 }
5579 5725
5580 5726 // 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
5581 5727
@@ -5581,14 +5727,8 @@
5581 5727
5582 5728 // Otherwise, go to a page waiting/polling for the callback. IOK 2018-05-16
5583 5729 wp_enqueue_script('check-vipps',plugins_url('js/check-order-status.js',__FILE__),array('jquery','vipps-gw'),filemtime(dirname(__FILE__) . "/js/check-order-status.js"), 'true');
5584 5730
5585 - // 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
5586 - // and check that the order matches (and is 'pending') (and exists)
5587 - $vippsstamp = $order->get_meta('_vipps_init_timestamp');
5588 - $vippsstatus = $order->get_meta('_vipps_status');
5589 - $message = __($order->get_meta('_vipps_confirm_message'),'woo-vipps');
5590 -
5591 5731 $signal = $this->callbackSignal($order);
5592 5732 $content = "";
5593 5733 $content .= "<div id='waiting'><p>" . sprintf(__('Waiting for confirmation of purchase from %1$s','woo-vipps'), $this->get_payment_method_name());
5594 5734
@@ -5621,93 +5761,21 @@
5621 5761 $content .= "<a id='continueToOrderFailed' style='display:none' href='" . $failure_redirect . "'></a>";
5622 5762 $content .= "<a id='continueToOrderFailedFallback' style='display:none' href='" . $gw->get_return_url($order) . "'></a>";
5623 5763 $content .= "</div>";
5624 5764
5765 + return $this->special_page_html('', $content);
5766 + }
5625 5767
5626 - $this->fakepage(__('Waiting for your order confirmation','woo-vipps'), $content);
5768 + // Returns formatted html for the vipps special page. LP 2026-08-27
5769 + public function special_page_html($header, $content) {
5770 + $header_html = $header ? "<h2 class='vipps-special-page-title page-title'>$header</h2>" : '';
5771 + $html = <<<EOF
5772 + $header_html
5773 + <div class="vipps-special-page-content">$content</div>
5774 + EOF;
5775 + return apply_filters('woo_vipps_special_page_html', $html, $header, $content);
5627 5776 }
5628 5777
5629 -
5630 -
5631 - public function fakepage($title,$content) {
5632 - global $wp, $wp_query;
5633 - // We don't want this here.
5634 - remove_filter ('the_content', 'wpautop');
5635 -
5636 - $specialid = $this->gateway()->get_option('vippsspecialpageid');
5637 - $wp_post = null;
5638 - if ($specialid) {
5639 - $wp_post = get_post($specialid);
5640 - if ($wp_post) {
5641 - $wp_post->post_title = $title;
5642 - $wp_post->post_content = $content;
5643 - // Normalize a bit
5644 - $wp_post->filter = 'raw'; // important
5645 - $wp_post->post_status = 'publish';
5646 - $wp_post->comment_status= 'closed';
5647 - $wp_post->ping_status= 'closed';
5648 - } else {
5649 - $this->log(sprintf(__("Could not use special page with id %s - it seems not to exist.", 'woo-vipps'), $specialid), 'error');
5650 - }
5651 - }
5652 - if (!$wp_post || is_wp_error($wp_post)) {
5653 - $post = new stdClass();
5654 - $post->ID = -99;
5655 - $post->post_author = 1;
5656 - $post->post_date = current_time( 'mysql' );
5657 - $post->post_date_gmt = current_time( 'mysql', 1 );
5658 - $post->post_title = $title;
5659 - $post->post_content = $content;
5660 - $post->post_status = 'publish';
5661 - $post->comment_status = 'closed';
5662 - $post->ping_status = 'closed';
5663 - $post->post_name = 'vippsconfirm-fake-page-name';
5664 - $post->post_type = 'page';
5665 - $post->filter = 'raw'; // important
5666 - $wp_post = new WP_Post($post);
5667 - wp_cache_add( -99, $wp_post, 'posts' );
5668 - }
5669 -
5670 - // Update the main query
5671 - $wp_query->post = $wp_post;
5672 - $wp_query->posts = array( $wp_post );
5673 - $wp_query->queried_object = $wp_post;
5674 - $wp_query->queried_object_id = $wp_post->ID;
5675 - $wp_query->found_posts = 1;
5676 - $wp_query->post_count = 1;
5677 - $wp_query->max_num_pages = 1;
5678 - $wp_query->is_page = true;
5679 - $wp_query->is_singular = true;
5680 - $wp_query->is_single = false;
5681 - $wp_query->is_attachment = false;
5682 - $wp_query->is_archive = false;
5683 - $wp_query->is_category = false;
5684 - $wp_query->is_tag = false;
5685 - $wp_query->is_tax = false;
5686 - $wp_query->is_author = false;
5687 - $wp_query->is_date = false;
5688 - $wp_query->is_year = false;
5689 - $wp_query->is_month = false;
5690 - $wp_query->is_day = false;
5691 - $wp_query->is_time = false;
5692 - $wp_query->is_search = false;
5693 - $wp_query->is_feed = false;
5694 - $wp_query->is_comment_feed = false;
5695 - $wp_query->is_trackback = false;
5696 - $wp_query->is_home = false;
5697 - $wp_query->is_embed = false;
5698 - $wp_query->is_404 = false;
5699 - $wp_query->is_paged = false;
5700 - $wp_query->is_admin = false;
5701 - $wp_query->is_preview = false;
5702 - $wp_query->is_robots = false;
5703 - $wp_query->is_posts_page = false;
5704 - $wp_query->is_post_type_archive = false;
5705 - // Update globals
5706 - $GLOBALS['wp_query'] = $wp_query;
5707 - $wp->register_globals();
5708 - return $wp_post;
5709 - }
5710 5778
5711 5779 // Support the interactivity API with data about our cart IOK 2026-02-23
5712 5780 public function woo_vipps_store_api_cart_data() {
5713 5781 // 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