| @@ -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 |