| @@ -125,8 +125,13 @@ | ||
| 125 | 125 | // Express Checkout and Checkout supports the new pickup_location shipping method, but the admin interface for this may |
| 126 | 126 | // not have loaded if the default checkout solution isn't the Checkout block. We'll load it anyway if the user has any local pickup locations |
| 127 | 127 | // stored in the database since we support this for both Vipps MobilePay checkokut and Express. IOK 2026-02-25 |
| 128 | 128 | add_action('woocommerce_load_shipping_methods', array($Vipps, 'maybe_load_pickup_locations'), 90); |
| 129 | + | |
| 130 | + // Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist | |
| 131 | + // Will also probably be used to maintain a real utility-page for Vipps actions later for themes where this | |
| 132 | + // is important. | |
| 133 | + add_filter('woocommerce_create_pages', array($Vipps, 'woocommerce_create_pages'), 50, 1); | |
| 129 | 134 | } |
| 130 | 135 | |
| 131 | 136 | // Register woocommerce store api endpoint to use in buy-now minicart block. LP 2026-02-10 |
| 132 | 137 | public function woocommerce_blocks_loaded() { |
| @@ -280,8 +285,38 @@ | ||
| 280 | 285 | } |
| 281 | 286 | |
| 282 | 287 | // Set default button options, migrating any older setup IOK 2026-07-15 |
| 283 | 288 | $this->init_button_options(); |
| 289 | + | |
| 290 | + /* | |
| 291 | + From version 6.2.x we create a real physical page to handle the "special" vipps pages, | |
| 292 | + where we earlier used just a fake page with no real page id, unless especially configured. | |
| 293 | + We therefore need to add code to maintain this special page. | |
| 294 | + | |
| 295 | + woocommerce_loaded is too early for this because of maybe_create_vipps_pages which calls WC_Install::create_pages, | |
| 296 | + and we hook unto this with woocommerce_create_pages. LP 2026-09-03 | |
| 297 | + */ | |
| 298 | + | |
| 299 | + // Delete special page id option when its deleted or trashed, so that we dont have to load | |
| 300 | + // in the post to check status in woocommerce_loaded when we ensure the special page exists. LP 2026-09-03 | |
| 301 | + $delete_special_page_id = function($post_id, $post = null) { | |
| 302 | + if (static::get_special_page_id() === $post_id) { | |
| 303 | + delete_option('woocommerce_vipps_special_page_page_id'); | |
| 304 | + } | |
| 305 | + }; | |
| 306 | + add_action('delete_post', $delete_special_page_id, 10, 2); | |
| 307 | + add_action('wp_trash_post', $delete_special_page_id, 10, 2); | |
| 308 | + | |
| 309 | + $this->ensure_special_page_exists(); | |
| 310 | + | |
| 311 | + | |
| 312 | + // We want this special page to have a certain title and maybe special scripts and so on, | |
| 313 | + // this gets run in template redirect for these pages. | |
| 314 | + add_action('woo_vipps_before_handling_special_page', array($this, 'pre_special_page_actions')); | |
| 315 | + | |
| 316 | + // Add an admin interface for this page as well IOK 2026-09-11 | |
| 317 | + add_action('woocommerce_settings_pages', array($this, 'woocommerce_settings_pages')); | |
| 318 | + | |
| 284 | 319 | } |
| 285 | 320 | |
| 286 | 321 | public function admin_init () { |
| 287 | 322 | $gw = $this->gateway(); |
| @@ -401,9 +436,77 @@ | ||
| 401 | 436 | } |
| 402 | 437 | } |
| 403 | 438 | } |
| 404 | 439 | |
| 440 | + | |
| 441 | + /** Ensure we have a special page for payment flows | |
| 442 | + * | |
| 443 | + * woocommerce_loaded is too early for this because of maybe_create_vipps_pages which calls WC_Install::create_pages, | |
| 444 | + * and we hook unto this with woocommerce_create_pages. LP 2026-09-03 | |
| 445 | + **/ | |
| 446 | + public function ensure_special_page_exists() { | |
| 447 | + if (static::get_special_page_id()) return; | |
| 448 | + $this->log(__('Missing id for special page, attempting to fix.', 'woo-vipps'), 'info'); | |
| 405 | 449 | |
| 450 | + // If user had in previous version overriden the fake page with a real one: migrate this page to be the special page. LP 2026-09-01 | |
| 451 | + $old_special_page_id = $this->gateway()->get_option('vippsspecialpageid'); | |
| 452 | + if ($old_special_page_id && ($special_page = get_post($old_special_page_id)) && "trash" !== $special_page->post_status) { | |
| 453 | + $this->log(__('Migrated old special page setting.', 'woo-vipps'), 'info'); | |
| 454 | + // there is no wc_set_page_id() so we update the option directly. LP 2026-09-01 | |
| 455 | + update_option('woocommerce_vipps_special_page_page_id', $old_special_page_id); | |
| 456 | + | |
| 457 | + // Ensure this page has the necessary shortcode. LP 2026-09-01 | |
| 458 | + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) { | |
| 459 | + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->"; | |
| 460 | + wp_update_post([ | |
| 461 | + 'ID' => $old_special_page_id, | |
| 462 | + 'post_content' => $new_content, | |
| 463 | + ]); | |
| 464 | + } | |
| 465 | + } else { | |
| 466 | + // Create special page if its missing. LP 2026-09-01 | |
| 467 | + $this->maybe_create_vipps_pages(); | |
| 468 | + } | |
| 469 | + } | |
| 470 | + | |
| 471 | + // Admin interface for the special page on woo/advanced/pages | |
| 472 | + public function woocommerce_settings_pages ($settings) { | |
| 473 | + $i = -1; | |
| 474 | + foreach($settings as $entry) { | |
| 475 | + $i++; | |
| 476 | + if ($entry['type'] == 'sectionend' && $entry['id'] == 'advanced_page_options') { | |
| 477 | + break; | |
| 478 | + } | |
| 479 | + } | |
| 480 | + if ($i > 0) { | |
| 481 | + $vippspagesettings = array( | |
| 482 | + array( | |
| 483 | + 'title' => sprintf(__( '%1$s Page', 'woo-vipps' ), Vipps::CompanyName()), | |
| 484 | + 'desc' => sprintf(__('This page is used for various special pages used by %1$s', 'woo-vipps'), Vipps::CompanyName()) . sprintf( __( 'Page contents: [%1$s]', 'woocommerce' ), 'vipps_special_page') , | |
| 485 | + 'id' => 'woocommerce_vipps_special_page_page_id', | |
| 486 | + 'type' => 'single_select_page_with_search', | |
| 487 | + 'default' => '', | |
| 488 | + 'class' => 'wc-page-search', | |
| 489 | + 'css' => 'min-width:300px;', | |
| 490 | + 'args' => array( | |
| 491 | + 'exclude' => | |
| 492 | + array( | |
| 493 | + wc_get_page_id( 'myaccount' ), | |
| 494 | + wc_get_page_id( 'checkout' ), | |
| 495 | + wc_get_page_id( 'cart' ), | |
| 496 | + ), | |
| 497 | + ), | |
| 498 | + 'desc_tip' => true, | |
| 499 | + 'autoload' => false, | |
| 500 | + )); | |
| 501 | + array_splice($settings, $i, 0, $vippspagesettings); | |
| 502 | + } | |
| 503 | + | |
| 504 | + return $settings; | |
| 505 | + } | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 406 | 509 | // Runs on init, adds the Vipps badge feature if activated |
| 407 | 510 | public function maybe_add_vipps_badge_feature () { |
| 408 | 511 | $badge_options = get_option('vipps_badge_options'); |
| 409 | 512 | if (!$badge_options || !@$badge_options['badgeon']) return false; |
| @@ -1164,9 +1267,9 @@ | ||
| 1164 | 1267 | |
| 1165 | 1268 | // Update the preview web component's attributes. LP 2026-06-24 |
| 1166 | 1269 | function updatePreview(event) { |
| 1167 | 1270 | const args = getPreviewArgs(); |
| 1168 | - // LP FIXME: when i use get_customer_language() here it gives me my user language, but on frontend it gives the site language, i.e not the same value. So this preview will be wrong language. so use get_locale for now. LP 2026-07-02 | |
| 1271 | + // FIXME: when i use get_customer_language() here it gives me my user language, but on frontend it gives the site language, i.e not the same value. So this preview will be wrong language. so use get_locale for now. LP 2026-07-02 | |
| 1169 | 1272 | // if ('store' === args.language) args.language = '<?php echo $this->get_customer_language(); ?>'; |
| 1170 | 1273 | if ('store' === args.language) args.language = '<?php echo substr(get_locale(), 0, 2); ?>'; |
| 1171 | 1274 | const button = jQuery('#vipps-button-express-preview'); |
| 1172 | 1275 | button.attr(args); |
| @@ -1704,8 +1807,11 @@ | ||
| 1704 | 1807 | // New vipps-mobilepay-badge shortcode. LP 19.11.2024 |
| 1705 | 1808 | add_shortcode('vipps-mobilepay-badge', array($this, 'vipps_mobilepay_badge_shortcode')); |
| 1706 | 1809 | // Legacy vipps-badge shortcode. LP 19.11.2024 |
| 1707 | 1810 | add_shortcode('vipps-badge', array($this, 'vipps_badge_shortcode')); |
| 1811 | + | |
| 1812 | + // special page handling, previously a fake page. LP 2026-08-25 | |
| 1813 | + add_shortcode('vipps_special_page', array($this, 'vipps_special_page_shortcode')); | |
| 1708 | 1814 | } |
| 1709 | 1815 | |
| 1710 | 1816 | |
| 1711 | 1817 | public function log ($what,$type='info') { |
| @@ -1731,8 +1837,10 @@ | ||
| 1731 | 1837 | } |
| 1732 | 1838 | |
| 1733 | 1839 | // Show express button option on checkout form. LP 2026-03-23 |
| 1734 | 1840 | public function checkout_before_customer_details_express () { |
| 1841 | + if (did_action('woo_vipps_checkout_before_customer_details_express')) return; | |
| 1842 | + do_action('woo_vipps_checkout_before_customer_details_express'); | |
| 1735 | 1843 | $gw = $this->gateway(); |
| 1736 | 1844 | if (!$gw->show_express_checkout()) return; |
| 1737 | 1845 | $this->express_checkout_section_html(); |
| 1738 | 1846 | } |
| @@ -2518,77 +2626,87 @@ | ||
| 2518 | 2626 | return null; |
| 2519 | 2627 | } |
| 2520 | 2628 | } |
| 2521 | 2629 | |
| 2630 | + // Special pages, and some callbacks. IOK 2018-05-18 | |
| 2631 | + public function template_redirect() { | |
| 2522 | 2632 | |
| 2523 | - // If this is a special page, return true very early because we are handling this. IOK 2023-02-22 | |
| 2524 | - public function pre_handle_404($current, $query) { | |
| 2525 | - if (!is_admin()) { | |
| 2526 | - $special = $this->is_special_page(); | |
| 2527 | - if ($special) { | |
| 2528 | - // Ensure very early on that Autooptimize does not try to optimize us (if installed) IOK 2023-03-04 | |
| 2529 | - add_filter( 'autoptimize_filter_noptimize', '__return_true'); | |
| 2530 | - return true; | |
| 2531 | - } | |
| 2633 | + // Handle legacy vipps-buy-now urls that auto-start express checkout for certain product - in QR codes etc IOK 2026-09-11 | |
| 2634 | + // We redirect these to the new location. | |
| 2635 | + $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); | |
| 2636 | + if (( ($_GET['VippsSpecialPage'] ?? '') == 'vipps-buy-product') || ($path && preg_match("!/vipps-buy-product/?$!", $path)) ) { | |
| 2637 | + $url = static::get_special_page_url(); | |
| 2638 | + $_GET['action'] = 'buy_product'; | |
| 2639 | + $q = build_query($_GET); | |
| 2640 | + wp_redirect($url . "?" . $q, 302); | |
| 2641 | + exit(); | |
| 2532 | 2642 | } |
| 2533 | - return $current; | |
| 2643 | + | |
| 2644 | + if (static::is_special_page()) { | |
| 2645 | + // Legacy: Stop the canonical redirect here. Unclear if still necessary. IOK 2026-09-11 | |
| 2646 | + remove_filter('template_redirect', 'redirect_canonical', 10); | |
| 2647 | + // dont cache special page. LP 2026-08-25 | |
| 2648 | + $this->nocache(); | |
| 2649 | + // Do the custom pre-load actions for these pages IOK 2026-09-11 | |
| 2650 | + do_action('woo_vipps_before_handling_special_page', ($_GET['action'] ?? "")); | |
| 2651 | + } | |
| 2534 | 2652 | } |
| 2535 | 2653 | |
| 2536 | - // Special pages, and some callbacks. IOK 2018-05-18 | |
| 2537 | - public function template_redirect() { | |
| 2538 | - global $post; | |
| 2539 | - // Handle special callbacks | |
| 2540 | - $special = $this->is_special_page() ; | |
| 2654 | + // Ran in template redirect for the special page. IOK 2026-09-2 | |
| 2655 | + public function pre_special_page_actions ($action) { | |
| 2656 | + // Change title dynamically depending on action. LP 2026-09-02 | |
| 2657 | + add_filter('the_title', [$this, 'vipps_special_page_endpoint_title'], 10, 2); | |
| 2541 | 2658 | |
| 2542 | - if ($special) { | |
| 2543 | - remove_filter('template_redirect', 'redirect_canonical', 10); | |
| 2544 | - do_action('woo_vipps_before_handling_special_page', $special); | |
| 2659 | + // If we are handling the 'wait for payment' action, we need to poll the order status before | |
| 2660 | + // we start producing content IOK 2026-09-21 | |
| 2661 | + if ($action == 'wait_for_payment') { | |
| 2662 | + $this->handle_payment_poll_and_redirect(); | |
| 2663 | + } | |
| 2545 | 2664 | |
| 2546 | - // Allow above hook to actually handle special pages. It should probably call $Vipps->fakepage or a redirect; can be used | |
| 2547 | - // to intercept express checkout etc. IOK 2022-03-18 | |
| 2548 | - if (! apply_filters('woo_vipps_special_page_handled', false, $special)) { | |
| 2549 | - $this->$special(); | |
| 2550 | - } | |
| 2665 | + // Some validation is required for this action | |
| 2666 | + if ($action == 'do_express_checkout') { | |
| 2667 | + $this->vipps_express_checkout_consistency_check(); | |
| 2551 | 2668 | } |
| 2669 | + } | |
| 2552 | 2670 | |
| 2553 | - $consentremoval = $this->is_consent_removal(); | |
| 2554 | - if ($consentremoval) { | |
| 2555 | - remove_filter('template_redirect', 'redirect_canonical', 10); | |
| 2556 | - do_action('woo_vipps_before_handling_special_page', 'consentremoval'); | |
| 2557 | - if (! apply_filters('woo_vipps_special_page_handled', false, 'consentremoval')) { | |
| 2558 | - $this->vipps_consent_removal_callback($consentremoval); | |
| 2671 | + | |
| 2672 | + // Dynamic special page title depending on endpoint/action, only frontend. LP 2026-09-02 | |
| 2673 | + public function vipps_special_page_endpoint_title($title, $postid = 0) { | |
| 2674 | + global $wp_query; | |
| 2675 | + // Comment from woocommerce's wc_page_endpoint_title where this logic is from: LP 2026-09-02 | |
| 2676 | + | |
| 2677 | + // In block themes the whole template (header, footer, content) renders inside the main | |
| 2678 | + // loop, so `the_title` fires for any post title rendered on the page (e.g. a product in a | |
| 2679 | + // server-rendered mini-cart) - not just the page's own heading. Only replace the title of | |
| 2680 | + // the queried page so an earlier title doesn't consume this one-shot filter. | |
| 2681 | + if ( ! is_null( $wp_query ) && ! is_admin() && is_main_query() && in_the_loop() && is_page() && $postid == static::get_special_page_id() ) { | |
| 2682 | + switch ($_GET['action'] ?? '') { | |
| 2683 | + case 'wait_for_payment': | |
| 2684 | + $title = __('Processing order', 'woo-vipps'); | |
| 2685 | + break; | |
| 2686 | + case 'do_express_checkout': | |
| 2687 | + case 'buy_product': | |
| 2688 | + $title = __('Express Checkout', 'woo-vipps'); | |
| 2689 | + break; | |
| 2559 | 2690 | } |
| 2560 | 2691 | } |
| 2692 | + return $title; | |
| 2561 | 2693 | } |
| 2694 | + | |
| 2562 | 2695 | // Template handling for special pages. IOK 2018-11-21 |
| 2696 | + // This is legacy - the special page is now a real page, so it can have a special template using standard WP methods. IOK 2026-09-11 | |
| 2563 | 2697 | public function template_include($template) { |
| 2564 | - $special = $this->is_special_page() ; | |
| 2565 | - if ($special) { | |
| 2698 | + if (static::is_special_page()) { | |
| 2566 | 2699 | // Get any special template override from the options IOK 2020-02-18 |
| 2567 | 2700 | $specific = $this->gateway()->get_option('vippsspecialpagetemplate'); |
| 2568 | 2701 | $found = locate_template($specific,false,false); |
| 2569 | 2702 | if ($found) $template=$found; |
| 2570 | 2703 | |
| 2571 | - return apply_filters('woo_vipps_special_page_template', $template, $special); | |
| 2704 | + return apply_filters('woo_vipps_special_page_template', $template, $_GET['action'] ?? ''); | |
| 2572 | 2705 | } |
| 2573 | 2706 | return $template; |
| 2574 | 2707 | } |
| 2575 | 2708 | |
| 2576 | - | |
| 2577 | - // Can't use wc-api for this, as that does not support DELETE . IOK 2018-05-18 | |
| 2578 | - private function is_consent_removal () { | |
| 2579 | - | |
| 2580 | - if ($_SERVER['REQUEST_METHOD'] != 'DELETE') return false; | |
| 2581 | - if ( !get_option('permalink_structure')) { | |
| 2582 | - if (@$_REQUEST['vipps-consent-removal']) return @$_REQUEST['callback']; | |
| 2583 | - return false; | |
| 2584 | - } | |
| 2585 | - if (preg_match("!/vipps-consent-removal/([^/]*)!", $_SERVER['REQUEST_URI'], $matches)) { | |
| 2586 | - return @$_REQUEST['callback']; | |
| 2587 | - } | |
| 2588 | - return false; | |
| 2589 | - } | |
| 2590 | - | |
| 2591 | 2709 | // On the thank you page, we have a completed order, so we need to restore any saved cart and possibly log in |
| 2592 | 2710 | // the user if using Express Checkout IOK 2020-10-09 |
| 2593 | 2711 | public function woocommerce_before_thankyou ($orderid) { |
| 2594 | 2712 | $order = wc_get_order($orderid); |
| @@ -2656,9 +2774,8 @@ | ||
| 2656 | 2774 | |
| 2657 | 2775 | // Support adding pickup locations to any shipping rate using the 'woo_vipps_shipping_method_pickup_points' filter |
| 2658 | 2776 | // IOK 2025-11-19 |
| 2659 | 2777 | add_filter('woo_vipps_modify_express_checkout_rate', array($this, 'express_add_pickup_location_options'), 10, 4); |
| 2660 | - | |
| 2661 | 2778 | } |
| 2662 | 2779 | |
| 2663 | 2780 | public function get_payment_method_name() { |
| 2664 | 2781 | return $this->gateway()->get_option('payment_method_name'); |
| @@ -2683,9 +2800,8 @@ | ||
| 2683 | 2800 | // Will also probably be used to maintain a real utility-page for Vipps actions later for themes where this |
| 2684 | 2801 | // is important. |
| 2685 | 2802 | add_filter('woocommerce_create_pages', array($this, 'woocommerce_create_pages'), 50, 1); |
| 2686 | 2803 | |
| 2687 | - | |
| 2688 | 2804 | // Callbacks use the Woo API IOK 2018-05-18 |
| 2689 | 2805 | add_action( 'woocommerce_api_wc_gateway_vipps', array($this,'vipps_callback')); |
| 2690 | 2806 | add_action( 'woocommerce_api_vipps_shipping_details', array($this,'vipps_shipping_details_callback')); |
| 2691 | 2807 | |
| @@ -2696,9 +2812,9 @@ | ||
| 2696 | 2812 | add_action( 'woocommerce_cart_actions', array($this, 'cart_express_checkout_button')); |
| 2697 | 2813 | add_action( 'woocommerce_widget_shopping_cart_buttons', array($this, 'minicart_express_checkout_button'), 30); |
| 2698 | 2814 | |
| 2699 | 2815 | // Previously we added an express html banner to the action 'woocommerce_before_checkout_form.', |
| 2700 | - // replaced by the new express buttons in manner more like Gutenberg. LP 2026-03-23 | |
| 2816 | + // replaced by the new express buttons in manner more like Gutenberg. for grepping: "express legacy checkout". LP 2026-03-23 | |
| 2701 | 2817 | add_action('woocommerce_checkout_before_customer_details', array($this, 'checkout_before_customer_details_express'), 5); |
| 2702 | 2818 | |
| 2703 | 2819 | add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button')); |
| 2704 | 2820 | add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20); |
| @@ -2703,12 +2819,10 @@ | ||
| 2703 | 2819 | add_action('woocommerce_after_add_to_cart_button', array($this, 'single_product_buy_now_button')); |
| 2704 | 2820 | add_action('woocommerce_after_shop_loop_item', array($this, 'loop_single_product_buy_now_button'), 20); |
| 2705 | 2821 | |
| 2706 | 2822 | |
| 2707 | - // Special pages and callbacks handled by template_redirect | |
| 2708 | - // We must also notify WP and other plugins that we are handling this 404-like situation. IOK 2023-02-22 | |
| 2823 | + // Special pages and callbacks handled by template_redirect. IOK 2023-02-22 | |
| 2709 | 2824 | add_action('template_redirect', array($this,'template_redirect'),1); |
| 2710 | - add_action('pre_handle_404', array($this, 'pre_handle_404'), 1, 2); | |
| 2711 | 2825 | |
| 2712 | 2826 | // Allow overriding their templates |
| 2713 | 2827 | add_filter('template_include', array($this,'template_include'), 10, 1); |
| 2714 | 2828 | |
| @@ -4120,17 +4234,8 @@ | ||
| 4120 | 4234 | header("X-Accel-Expires: 0"); |
| 4121 | 4235 | } |
| 4122 | 4236 | |
| 4123 | 4237 | |
| 4124 | - | |
| 4125 | - // Handle DELETE on a vipps consent removal callback | |
| 4126 | - public function vipps_consent_removal_callback ($callback) { | |
| 4127 | - Vipps::nocache(); | |
| 4128 | - // Currently, no such requests will be posted, and as this code isn't sufficiently tested,we'll just have | |
| 4129 | - // to escape here when the API is changed. IOK 2020-10-14 | |
| 4130 | - $this->log("Consent removal is non-functional pending API changes as of 2020-10-14"); print "1"; exit(); | |
| 4131 | - } | |
| 4132 | - | |
| 4133 | 4238 | public function woocommerce_payment_gateways($methods) { |
| 4134 | 4239 | require_once(dirname(__FILE__) . "/WC_Gateway_Vipps.class.php"); |
| 4135 | 4240 | require_once(dirname(__FILE__) . "/WC_Gateway_VippsCard.class.php"); |
| 4136 | 4241 | // Protect the singleton: Use the object instead of the class name IOK 2025-02-04 |
| @@ -4155,9 +4260,11 @@ | ||
| 4155 | 4260 | if ( empty($_REQUEST['add-to-cart']) || ! is_numeric($_REQUEST['add-to-cart']) || empty($_REQUEST['vipps_compat_mode']) || !$_REQUEST['vipps_compat_mode']) { |
| 4156 | 4261 | return $url; |
| 4157 | 4262 | } |
| 4158 | 4263 | $url = $this->express_checkout_url(); |
| 4159 | - $url = wp_nonce_url($url,'express','sec'); | |
| 4264 | + // At this point, there is always a query argument here. IOK 2026-09-21 | |
| 4265 | + $nonce = wp_create_nonce('express'); | |
| 4266 | + $url = $url . "&sec=$nonce"; | |
| 4160 | 4267 | |
| 4161 | 4268 | return $url; |
| 4162 | 4269 | } |
| 4163 | 4270 | |
| @@ -4253,20 +4360,40 @@ | ||
| 4253 | 4360 | } |
| 4254 | 4361 | } |
| 4255 | 4362 | |
| 4256 | 4363 | public function activate () { |
| 4257 | - static::maybe_add_cron_event(); | |
| 4258 | - $gw = $this->gateway(); | |
| 4364 | + static::maybe_add_cron_event(); | |
| 4365 | + $gw = $this->gateway(); | |
| 4259 | 4366 | |
| 4260 | - // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19 | |
| 4261 | - if ($gw->get_option('orderprefix') == 'Woo') { | |
| 4262 | - $gw->update_option('orderprefix', $this->generate_order_prefix()); | |
| 4263 | - } | |
| 4264 | - // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point. | |
| 4265 | - $gw->initialize_webhooks(); | |
| 4266 | - $this->payment_method_name = $gw->get_option('payment_method_name'); | |
| 4267 | - } | |
| 4367 | + // If store is using the default "Woo" orderprefix, generate a new one, this time using the stores' sitename if possible. IOK 2020-05-19 | |
| 4368 | + if ($gw->get_option('orderprefix') == 'Woo') { | |
| 4369 | + $gw->update_option('orderprefix', $this->generate_order_prefix()); | |
| 4370 | + } | |
| 4371 | + // IOK 2023-12-20 for the epayment api, we need to re-initialize webhooks at this point. | |
| 4372 | + $gw->initialize_webhooks(); | |
| 4373 | + $this->payment_method_name = $gw->get_option('payment_method_name'); | |
| 4268 | 4374 | |
| 4375 | + | |
| 4376 | + // Check if the special page is noted and actually does exist | |
| 4377 | + $special = static::get_special_page_id(); | |
| 4378 | + if ($special) { | |
| 4379 | + $special_page = get_post($special); | |
| 4380 | + if ($special_page && 'trash' !== $special_page->post_status) { | |
| 4381 | + // Ensure this page has the necessary shortcode. LP 2026-09-01 | |
| 4382 | + if (!has_shortcode($special_page->post_content, 'vipps_special_page')) { | |
| 4383 | + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->"; | |
| 4384 | + wp_update_post([ | |
| 4385 | + 'ID' => $special, | |
| 4386 | + 'post_content' => $new_content, | |
| 4387 | + ]); | |
| 4388 | + } | |
| 4389 | + } else { | |
| 4390 | + delete_option('woocommerce_vipps_special_page_page_id'); | |
| 4391 | + } | |
| 4392 | + } | |
| 4393 | + | |
| 4394 | + } | |
| 4395 | + | |
| 4269 | 4396 | // We have added some hooks to wp-cron; remove these. IOK 2020-04-01 |
| 4270 | 4397 | public static function deactivate() { |
| 4271 | 4398 | $timestamp = wp_next_scheduled('vipps_cron_cleanup_hook'); |
| 4272 | 4399 | wp_unschedule_event($timestamp, 'vipps_cron_cleanup_hook'); |
| @@ -4319,9 +4446,10 @@ | ||
| 4319 | 4446 | // If setting is true, use Vipps as default payment. Called by the woocommrece_cart_updated hook. IOK 2018-06-06 |
| 4320 | 4447 | private function maybe_set_vipps_as_default() { |
| 4321 | 4448 | if (WC()->session->get('chosen_payment_method')) return; // User has already chosen payment method, so we're done. |
| 4322 | 4449 | $gw = $this->gateway(); |
| 4323 | - if ($gw->get_option('vippsdefault')=='yes') { | |
| 4450 | + // Do *not* default to vipps if Kustom Checkout is installed IOK 2026-09-11 | |
| 4451 | + if ($gw->get_option('vippsdefault')=='yes' && !class_exists('KCO')) { | |
| 4324 | 4452 | WC()->session->set('chosen_payment_method', $gw->id); |
| 4325 | 4453 | } |
| 4326 | 4454 | } |
| 4327 | 4455 | |
| @@ -4569,9 +4697,10 @@ | ||
| 4569 | 4697 | } |
| 4570 | 4698 | if (!$o) return; |
| 4571 | 4699 | if (!$o->get_meta('_vipps_single_product_express')) return; |
| 4572 | 4700 | if ($failed && !apply_filters('woo_vipps_restore_cart_on_express_checkout_failure', true, $o)) return; |
| 4573 | - if ($failed) WC()->cart->empty_cart(); | |
| 4701 | + // Restoring cart! But clear it first so we dont add this single product to the restored cart. LP 2026-09-22 | |
| 4702 | + WC()->cart->empty_cart(); | |
| 4574 | 4703 | $this->restore_cart($o); |
| 4575 | 4704 | } |
| 4576 | 4705 | |
| 4577 | 4706 | |
| @@ -4927,46 +5056,37 @@ | ||
| 4927 | 5056 | wp_send_json(array('status'=>'error', 'msg'=> __('Unknown payment status','woo-vipps') . ' ' . $payment)); |
| 4928 | 5057 | return false; |
| 4929 | 5058 | } |
| 4930 | 5059 | |
| 4931 | - // The various return URLs for special pages of the Vipps stuff depend on settings and pretty-URLs so we supply them from here | |
| 4932 | - // These are for the "fallback URL" mostly. IOK 2018-05-18 | |
| 4933 | - private function make_vipps_url($what) { | |
| 4934 | - if ( !get_option('permalink_structure')) { | |
| 4935 | - return add_query_arg('VippsSpecialPage', $what, home_url("/", 'https')); | |
| 4936 | - } | |
| 4937 | - return trailingslashit(home_url($what, 'https')); | |
| 5060 | + // 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 | |
| 5061 | + private function make_special_page_url($action) { | |
| 5062 | + return add_query_arg('action', $action, $this->get_special_page_url()); | |
| 4938 | 5063 | } |
| 5064 | + | |
| 4939 | 5065 | public function payment_return_url() { |
| 4940 | - return apply_filters('woo_vipps_payment_return_url', $this->make_vipps_url('vipps-betaling')); | |
| 5066 | + return apply_filters('woo_vipps_payment_return_url', $this->make_special_page_url('wait_for_payment')); | |
| 4941 | 5067 | } |
| 4942 | 5068 | public function express_checkout_url() { |
| 4943 | - return $this->make_vipps_url('vipps-express-checkout'); | |
| 5069 | + return $this->make_special_page_url('do_express_checkout'); | |
| 4944 | 5070 | } |
| 4945 | 5071 | public function buy_product_url() { |
| 4946 | - return $this->make_vipps_url('vipps-buy-product'); | |
| 5072 | + return $this->make_special_page_url('buy_product'); | |
| 4947 | 5073 | } |
| 4948 | 5074 | |
| 4949 | - // Return the method in the Vipps | |
| 4950 | - public function is_special_page() { | |
| 4951 | - $specials = array('vipps-betaling' => 'vipps_wait_for_payment', 'vipps-express-checkout'=>'vipps_express_checkout', 'vipps-buy-product'=>'vipps_buy_product'); | |
| 4952 | - $method = null; | |
| 4953 | - if ( get_option('permalink_structure')) { | |
| 4954 | - foreach($specials as $special=>$specialmethod) { | |
| 4955 | - // IOK 2018-06-07 Change to add any prefix from home-url for better matching IOK 2018-06-07 | |
| 4956 | - $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); | |
| 4957 | - if ($path && preg_match("!/$special/?$!", $path, $matches)) { | |
| 4958 | - $method = $specialmethod; break; | |
| 4959 | - } | |
| 4960 | - } | |
| 4961 | - } else { | |
| 4962 | - if (isset($_GET['VippsSpecialPage'])) { | |
| 4963 | - $method = @$specials[$_GET['VippsSpecialPage']]; | |
| 4964 | - } | |
| 4965 | - } | |
| 4966 | - return $method; | |
| 5075 | + public static function is_special_page() { | |
| 5076 | + $id = static::get_special_page_id(); | |
| 5077 | + return $id && is_page($id); | |
| 4967 | 5078 | } |
| 4968 | 5079 | |
| 5080 | + public static function get_special_page_id() { | |
| 5081 | + $id = wc_get_page_id('vipps_special_page'); // -1 if not found | |
| 5082 | + return $id > 0 ? $id : null; | |
| 5083 | + } | |
| 5084 | + | |
| 5085 | + public static function get_special_page_url() { | |
| 5086 | + return get_permalink(static::get_special_page_id()); | |
| 5087 | + } | |
| 5088 | + | |
| 4969 | 5089 | // Just create a spinner and a overlay. |
| 4970 | 5090 | public function spinner () { |
| 4971 | 5091 | $flavour = sanitize_title($this->get_payment_method_name()); |
| 4972 | 5092 | ob_start(); |
| @@ -5148,41 +5268,88 @@ | ||
| 5148 | 5268 | |
| 5149 | 5269 | |
| 5150 | 5270 | // Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist |
| 5151 | 5271 | // IOK 2026-04-30 remove this when checkout is end-of-life'd |
| 5272 | + // We now also use this for the vipps special page, previously a fakepage. LP 2026-08-18 | |
| 5152 | 5273 | public function woocommerce_create_pages ($data) { |
| 5274 | + // Vipps Checkout page | |
| 5153 | 5275 | $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false); |
| 5154 | - if (!$vipps_checkout_activated) return $data; | |
| 5276 | + if ($vipps_checkout_activated) { | |
| 5277 | + $data['vipps_checkout'] = array( | |
| 5278 | + 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ), | |
| 5279 | + 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ), | |
| 5280 | + 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->', | |
| 5281 | + ); | |
| 5282 | + } | |
| 5155 | 5283 | |
| 5156 | - $data['vipps_checkout'] = array( | |
| 5157 | - 'name' => _x( 'vipps_checkout', 'Page slug', 'woo-vipps' ), | |
| 5158 | - 'title' => _x( 'Vipps MobilePay Checkout', 'Page title', 'woo-vipps' ), | |
| 5159 | - 'content' => '<!-- wp:shortcode -->[' . 'vipps_checkout' . ']<!-- /wp:shortcode -->', | |
| 5160 | - ); | |
| 5161 | - | |
| 5284 | + // Vipps special page for certain payment flow actions. Previously a fake page. LP 2026-08-18 | |
| 5285 | + $data['vipps_special_page'] = [ | |
| 5286 | + 'name' => 'vipps-payment', // slug | |
| 5287 | + /* translators: company name */ | |
| 5288 | + 'title' => sprintf(__('%s special page', 'woo-vipps'), static::CompanyName()), // we hide the title frontend in template_redirect. LP 2026-08-27 | |
| 5289 | + 'content' => '<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->', | |
| 5290 | + ]; | |
| 5162 | 5291 | return $data; |
| 5163 | 5292 | } |
| 5164 | 5293 | |
| 5165 | - // Creates any necessary Vipps pages. Will be called e.g. when activating Checkout or turning it on. | |
| 5294 | + // Creates any necessary Vipps pages. E.g vipps checkout page or vipps special page. LP 2026-09-01 | |
| 5295 | + // If a page slug already exists, then it won't overwrite or duplicate it!. LP 2026-09-02 | |
| 5166 | 5296 | public function maybe_create_vipps_pages () { |
| 5297 | + $make_pages = false; | |
| 5298 | + | |
| 5299 | + // Vipps Checkout page. LP 2026-08-18 | |
| 5167 | 5300 | $checkoutid = wc_get_page_id('vipps_checkout'); |
| 5168 | - $makeit = !$checkoutid || ! get_post_status($checkoutid); | |
| 5169 | - if ($makeit) { | |
| 5301 | + if (!$checkoutid || ! get_post_status($checkoutid)) { | |
| 5170 | 5302 | delete_option('woocommerce_vipps_checkout_page_id'); |
| 5303 | + $make_pages = true; | |
| 5171 | 5304 | } |
| 5172 | 5305 | |
| 5173 | - if ($makeit) { | |
| 5174 | - WC_Install::create_pages(); | |
| 5306 | + // vipps special page, previously a fake page. LP 2026-08-18 | |
| 5307 | + $builtin_special_page_id = static::get_special_page_id(); | |
| 5308 | + if (!$builtin_special_page_id || !get_post_status($builtin_special_page_id)) { | |
| 5309 | + delete_option('woocommerce_vipps_special_page_page_id'); | |
| 5310 | + $make_pages = true; | |
| 5175 | 5311 | } |
| 5312 | + | |
| 5313 | + if ($make_pages) { | |
| 5314 | + WC_Install::create_pages(); | |
| 5315 | + } | |
| 5176 | 5316 | } |
| 5177 | 5317 | |
| 5318 | + public function vipps_special_page_shortcode($atts, $content) { | |
| 5319 | + // No point in expanding this unless we are actually doing the special actions. LP 2026-08-25 | |
| 5320 | + if (is_admin()) return; | |
| 5321 | + if (wp_doing_ajax()) return; | |
| 5322 | + if (defined('REST_REQUEST') && REST_REQUEST) return; | |
| 5323 | + if (did_filter('woo_vipps_special_page_html')) return; // User has somehow added two shortcodes. IOK 2026-09-18 | |
| 5178 | 5324 | |
| 5325 | + $action = $_GET['action'] ?? ''; | |
| 5326 | + $html = ""; | |
| 5327 | + switch ($action) { | |
| 5328 | + case 'wait_for_payment': | |
| 5329 | + $html = $this->vipps_wait_for_payment(); | |
| 5330 | + break; | |
| 5331 | + case 'do_express_checkout': | |
| 5332 | + $html = $this->vipps_express_checkout(); | |
| 5333 | + break; | |
| 5334 | + case 'buy_product': | |
| 5335 | + $html = $this->vipps_buy_product(); | |
| 5336 | + break; | |
| 5337 | + default: | |
| 5338 | + $html = ''; | |
| 5339 | + } | |
| 5340 | + // This is mostly to avoid this shortcode evaluating twice IOK 2026-09-18 | |
| 5341 | + $html = apply_filters('woo_vipps_special_page_html', $html, $action); | |
| 5342 | + | |
| 5343 | + // Remember, this is a shortcode, so the html must be returned, not echoed IOK 2026-09-11 | |
| 5344 | + return $html; | |
| 5345 | + } | |
| 5346 | + | |
| 5347 | + | |
| 5179 | 5348 | // This URL will when accessed add a product to the cart and go directly to the express checkout page. |
| 5180 | 5349 | // The argument passed must be a shareable link created for a given product - so this in effect acts as a landing page for |
| 5181 | 5350 | // the buying thru Vipps Express Checkout of a single product linked to in for instance banners. IOK 2018-09-24 |
| 5182 | 5351 | public function vipps_buy_product() { |
| 5183 | - status_header(200,'OK'); | |
| 5184 | - Vipps::nocache(); | |
| 5185 | 5352 | |
| 5186 | 5353 | add_filter('body_class', function ($classes) { |
| 5187 | 5354 | $classes[] = 'vipps-express-checkout'; |
| 5188 | 5355 | $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24 |
| @@ -5218,9 +5385,9 @@ | ||
| 5218 | 5385 | |
| 5219 | 5386 | if (!$productinfo) { |
| 5220 | 5387 | $title = __("Product is no longer available",'woo-vipps'); |
| 5221 | 5388 | $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'); |
| 5222 | - return $this->fakepage($title,$content); | |
| 5389 | + return $this->special_page_html($title,$content); | |
| 5223 | 5390 | } |
| 5224 | 5391 | |
| 5225 | 5392 | // Pass the productinfo to the express checkout form |
| 5226 | 5393 | $args = array(); |
| @@ -5237,20 +5404,16 @@ | ||
| 5237 | 5404 | } |
| 5238 | 5405 | $args[sanitize_title(wp_unslash($key))] = sanitize_text_field(wp_unslash($value)); |
| 5239 | 5406 | } |
| 5240 | 5407 | |
| 5241 | - $this->print_express_checkout_page(true,'do_single_product_express_checkout',$args); | |
| 5408 | + return $this->express_checkout_page_html(true,'do_single_product_express_checkout',$args); | |
| 5242 | 5409 | } |
| 5243 | 5410 | |
| 5244 | - // 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. | |
| 5245 | - public function vipps_express_checkout() { | |
| 5246 | - status_header(200,'OK'); | |
| 5247 | - Vipps::nocache(); | |
| 5411 | + public function vipps_express_checkout_consistency_check() { | |
| 5248 | 5412 | // We need a nonce to get here, but we should only get here when we have a cart, so this will not be cached. |
| 5249 | 5413 | // IOK 2018-05-28 |
| 5250 | 5414 | $ok = isset($_REQUEST['sec']) && wp_verify_nonce($_REQUEST['sec'],'express'); |
| 5251 | 5415 | |
| 5252 | - | |
| 5253 | 5416 | $backurl = wp_validate_redirect(@$_SERVER['HTTP_REFERER']); |
| 5254 | 5417 | if (!$backurl) $backurl = home_url(); |
| 5255 | 5418 | |
| 5256 | 5419 | if (!$ok) { |
| @@ -5264,8 +5427,19 @@ | ||
| 5264 | 5427 | wp_redirect($backurl); |
| 5265 | 5428 | exit(); |
| 5266 | 5429 | } |
| 5267 | 5430 | |
| 5431 | + add_filter('woo_vipps_express_checkout_consistent', '__return_true'); | |
| 5432 | + } | |
| 5433 | + | |
| 5434 | + // 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. | |
| 5435 | + public function vipps_express_checkout() { | |
| 5436 | + // Some checks are made in template_redirect, we check here if they are ok IOK 2026-09-21 | |
| 5437 | + if (!apply_filters('woo_vipps_express_checkout_consistent', false)) { | |
| 5438 | + $content = __('Link expired, please try again', 'woo-vipps'); | |
| 5439 | + return $content; | |
| 5440 | + } | |
| 5441 | + | |
| 5268 | 5442 | add_filter('body_class', function ($classes) { |
| 5269 | 5443 | $classes[] = 'vipps-express-checkout'; |
| 5270 | 5444 | $classes[] = 'woocommerce-checkout'; // Required by Pixel Your Site IOK 2022-11-24 |
| 5271 | 5445 | return apply_filters('woo_vipps_express_checkout_body_class', $classes); |
| @@ -5272,9 +5446,9 @@ | ||
| 5272 | 5446 | }); |
| 5273 | 5447 | |
| 5274 | 5448 | do_action('woo_vipps_express_checkout_page'); |
| 5275 | 5449 | |
| 5276 | - $this->print_express_checkout_page(true, 'do_express_checkout'); | |
| 5450 | + return $this->express_checkout_page_html(true, 'do_express_checkout'); | |
| 5277 | 5451 | } |
| 5278 | 5452 | |
| 5279 | 5453 | // This method tries to ensure that a customer does not 'lose' the return page and |
| 5280 | 5454 | // starts ordering the same products twice. IOK 2020-01-22 |
| @@ -5369,9 +5543,10 @@ | ||
| 5369 | 5543 | return $orderspec; |
| 5370 | 5544 | } |
| 5371 | 5545 | |
| 5372 | 5546 | // Used as a landing page for launching express checkout - borh for the cart and for single products. IOK 2018-09-28 |
| 5373 | - protected function print_express_checkout_page($execute,$action,$productinfo=null) { | |
| 5547 | + // Returns the html. LP 2026-08-27 | |
| 5548 | + protected function express_checkout_page_html($execute,$action,$productinfo=null) { | |
| 5374 | 5549 | $gw = $this->gateway(); |
| 5375 | 5550 | |
| 5376 | 5551 | $expressCheckoutMessages = array(); |
| 5377 | 5552 | $expressCheckoutMessages['termsAndConditionsError'] = __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' ); |
| @@ -5454,10 +5629,9 @@ | ||
| 5454 | 5629 | |
| 5455 | 5630 | if ($execute) { |
| 5456 | 5631 | $content .= "<p id=waiting>" . __("Please wait while we are preparing your order", 'woo-vipps') . "</p>"; |
| 5457 | 5632 | $content .= "<div id='vipps-status-message'></div>"; |
| 5458 | - $this->fakepage(__('Order in progress','woo-vipps'), $content); | |
| 5459 | - return; | |
| 5633 | + return $this->special_page_html('', $content); | |
| 5460 | 5634 | } else { |
| 5461 | 5635 | $content .= $askForConfirmationHTML; |
| 5462 | 5636 | $content .= $extraHTML; |
| 5463 | 5637 | $content .= $termsHTML; |
| @@ -5464,21 +5638,16 @@ | ||
| 5464 | 5638 | $content .= apply_filters('woo_vipps_express_checkout_validation_elements', ''); |
| 5465 | 5639 | $title = sprintf(__('Buy now with %1$s!', 'woo-vipps'), $this->get_payment_method_name()); |
| 5466 | 5640 | $content .= "<div class='vipps_buy_now_wrapper noloop'><a href='#' id='do-express-checkout' class='vipps-express-checkout' title='$title'>$buttonhtml</a></div>"; |
| 5467 | 5641 | $content .= "<div id='vipps-status-message'></div>"; |
| 5468 | - $this->fakepage(sprintf(__('%1$s Express Checkout','woo-vipps'), $this->get_payment_method_name()), $content); | |
| 5469 | - return; | |
| 5642 | + return $this->special_page_html('', $content); | |
| 5470 | 5643 | } |
| 5471 | 5644 | } |
| 5472 | 5645 | |
| 5473 | 5646 | |
| 5474 | - | |
| 5475 | - public function vipps_wait_for_payment() { | |
| 5476 | - status_header(200,'OK'); | |
| 5477 | - Vipps::nocache(); | |
| 5478 | - | |
| 5647 | + // Called in template_redirect before we get to the wait-for-payment page IOK 2026-09-21 | |
| 5648 | + private function handle_payment_poll_and_redirect () { | |
| 5479 | 5649 | $orderid = WC()->session->get('_vipps_pending_order'); |
| 5480 | - | |
| 5481 | 5650 | $order = null; |
| 5482 | 5651 | $gw = $this->gateway(); |
| 5483 | 5652 | |
| 5484 | 5653 | // Failsafe for when the session disappears IOK 2018-11-19 |
| @@ -5490,9 +5659,9 @@ | ||
| 5490 | 5659 | // If so, we will read the order id from the GET arguments and check if the auth token is correct, |
| 5491 | 5660 | // simulating the session with that. |
| 5492 | 5661 | // IOK 2019-11-19, changed to using GET 2023-01-23 |
| 5493 | 5662 | if ($no_session && $limited_session) { |
| 5494 | - $orderid = intval(@$_GET['id']); | |
| 5663 | + $orderid = intval($_GET['id'] ?? false); | |
| 5495 | 5664 | } |
| 5496 | 5665 | if ($orderid) { |
| 5497 | 5666 | clean_post_cache($orderid); |
| 5498 | 5667 | $order = wc_get_order($orderid); |
| @@ -5511,11 +5680,8 @@ | ||
| 5511 | 5680 | $session->set('_vipps_pending_order', $orderid); |
| 5512 | 5681 | } |
| 5513 | 5682 | } |
| 5514 | 5683 | |
| 5515 | - | |
| 5516 | - do_action('woo_vipps_wait_for_payment_page',$order); | |
| 5517 | - | |
| 5518 | 5684 | $deleted_order=0; |
| 5519 | 5685 | if ($orderid && !$order) { |
| 5520 | 5686 | // If this happens, we actually did have an order, but it has been deleted, which must mean that it was cancelled. |
| 5521 | 5687 | // Concievably a hook on the 'cancel'-transition or in the callback handlers could clean that up before we get here. IOK 2019-09-26 |
| @@ -5540,9 +5706,9 @@ | ||
| 5540 | 5706 | clean_post_cache($orderid); |
| 5541 | 5707 | $order = wc_get_order($orderid); // Reload order object |
| 5542 | 5708 | } |
| 5543 | 5709 | } else { |
| 5544 | - // No need to do anyting here. IOK 2020-01-26 | |
| 5710 | + // No need to do anyting here. IOK 2020-01-26 | |
| 5545 | 5711 | } |
| 5546 | 5712 | |
| 5547 | 5713 | $payment = 'notchecked'; |
| 5548 | 5714 | if ($do_poll) { |
| @@ -5558,9 +5724,8 @@ | ||
| 5558 | 5724 | exit(); |
| 5559 | 5725 | } |
| 5560 | 5726 | |
| 5561 | 5727 | // We are done, but in failure. Don't poll. |
| 5562 | - $content = ""; | |
| 5563 | 5728 | $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid); |
| 5564 | 5729 | |
| 5565 | 5730 | // Status is failed; still send to return url (as of now /order-recieved), the text there will depend on the status. |
| 5566 | 5731 | // For failed it shows a "Retry payment" button that takes the customer to /pay-for-order where it will be retried. LP 2026-03-17 |
| @@ -5568,8 +5733,9 @@ | ||
| 5568 | 5733 | $failure_redirect = $failure_redirect ?: $gw->get_return_url($order); |
| 5569 | 5734 | wp_redirect($failure_redirect); |
| 5570 | 5735 | exit(); |
| 5571 | 5736 | } |
| 5737 | + | |
| 5572 | 5738 | if ($status == 'cancelled' || $payment == 'cancelled') { |
| 5573 | 5739 | $this->maybe_restore_cart($orderid,'failed'); |
| 5574 | 5740 | if ($failure_redirect){ |
| 5575 | 5741 | wp_redirect($failure_redirect); |
| @@ -5574,27 +5740,45 @@ | ||
| 5574 | 5740 | if ($failure_redirect){ |
| 5575 | 5741 | wp_redirect($failure_redirect); |
| 5576 | 5742 | exit(); |
| 5577 | 5743 | } |
| 5744 | + } else { | |
| 5745 | + // If not, enqueue the status checker IOK 2026-09-21 | |
| 5746 | + 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'); | |
| 5747 | + } | |
| 5748 | + | |
| 5749 | + // Communicate this to the shortcode IOK 2026-09-21 | |
| 5750 | + add_filter('woo_vipps_wait_for_payment_status', function () use($orderid, $status, $payment) { | |
| 5751 | + return ['orderid'=>$orderid, 'status'=>$status, 'payment'=>$payment]; | |
| 5752 | + }); | |
| 5753 | + | |
| 5754 | + } | |
| 5755 | + | |
| 5756 | + public function vipps_wait_for_payment() { | |
| 5757 | + | |
| 5758 | + // This will have been computed in template_redirect, but the status will be either still pending or failed. IOK 2026-09-21 | |
| 5759 | + $data = apply_filters('woo_vipps_wait_for_payment_status', []); | |
| 5760 | + | |
| 5761 | + $orderid = $data['orderid'] ?? 0; | |
| 5762 | + $status = $data['status'] ?? ""; | |
| 5763 | + $payment = $data['payment'] ?? ""; | |
| 5764 | + | |
| 5765 | + $order = wc_get_order($orderid); | |
| 5766 | + if (!$order) wp_die(__('Unknown order', 'woo-vipps')); | |
| 5767 | + | |
| 5768 | + do_action('woo_vipps_wait_for_payment_page',$order); | |
| 5769 | + $gw = $this->gateway(); | |
| 5770 | + | |
| 5771 | + $content = ""; | |
| 5772 | + if ($status == 'cancelled' || $payment == 'cancelled') { | |
| 5578 | 5773 | $content .= "<div id=failure><p>". __('Order cancelled','woo-vipps') . '</p>'; |
| 5579 | 5774 | $content .= "<p><a href='" . home_url() . "' class='btn button'>" . __('Continue shopping','woo-vipps') . '</a></p>'; |
| 5580 | 5775 | $content .= "</div>"; |
| 5581 | - $this->fakepage(__('Order cancelled','woo-vipps'), $content); | |
| 5582 | - | |
| 5583 | - return; | |
| 5776 | + return $this->special_page_html('', $content); | |
| 5584 | 5777 | } |
| 5585 | 5778 | |
| 5586 | 5779 | // 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 |
| 5587 | - | |
| 5588 | 5780 | // Otherwise, go to a page waiting/polling for the callback. IOK 2018-05-16 |
| 5589 | - 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'); | |
| 5590 | - | |
| 5591 | - // 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 | |
| 5592 | - // and check that the order matches (and is 'pending') (and exists) | |
| 5593 | - $vippsstamp = $order->get_meta('_vipps_init_timestamp'); | |
| 5594 | - $vippsstatus = $order->get_meta('_vipps_status'); | |
| 5595 | - $message = __($order->get_meta('_vipps_confirm_message'),'woo-vipps'); | |
| 5596 | - | |
| 5597 | 5781 | $signal = $this->callbackSignal($order); |
| 5598 | 5782 | $content = ""; |
| 5599 | 5783 | $content .= "<div id='waiting'><p>" . sprintf(__('Waiting for confirmation of purchase from %1$s','woo-vipps'), $this->get_payment_method_name()); |
| 5600 | 5784 | |
| @@ -5602,15 +5786,16 @@ | ||
| 5602 | 5786 | $signalurl = $this->callbackSignalURL($signal); |
| 5603 | 5787 | |
| 5604 | 5788 | $content .= "</p></div>"; |
| 5605 | 5789 | |
| 5606 | - // We impersonate the woocommerce-checkout form here mainly to work with the Pixel Your Site plugin IOK 2022-11-24 | |
| 5607 | - $classlist = apply_filters("woo_vipps_express_checkout_form_classes", "woocommerce-checkout"); | |
| 5608 | - $content .= "<form id='vippsdata' class='" . esc_attr($classlist) . "'>"; | |
| 5790 | + $failure_redirect = apply_filters('woo_vipps_order_failed_redirect', '', $orderid); | |
| 5791 | + | |
| 5792 | + // Carry the order status to the checking script IOK 2026-09-21 | |
| 5793 | + $content .= "<form id='vippsdata'>"; | |
| 5609 | 5794 | $content .= "<input type='hidden' id='fkey' name='fkey' value='".htmlspecialchars($signalurl)."'>"; |
| 5610 | 5795 | $content .= "<input type='hidden' name='key' value='".htmlspecialchars($order->get_order_key())."'>"; |
| 5611 | 5796 | $content .= "<input type='hidden' name='action' value='check_order_status'>"; |
| 5612 | - $content .= wp_nonce_field('vippsstatus','sec',1,false); | |
| 5797 | + $content .= wp_nonce_field('vippsstatus','sec',1,false); | |
| 5613 | 5798 | $content .= "</form>"; |
| 5614 | 5799 | |
| 5615 | 5800 | |
| 5616 | 5801 | $content .= "<div id='error' style='display:none'><p>".__('Error during order confirmation','woo-vipps'). '</p>'; |
| @@ -5627,93 +5812,21 @@ | ||
| 5627 | 5812 | $content .= "<a id='continueToOrderFailed' style='display:none' href='" . $failure_redirect . "'></a>"; |
| 5628 | 5813 | $content .= "<a id='continueToOrderFailedFallback' style='display:none' href='" . $gw->get_return_url($order) . "'></a>"; |
| 5629 | 5814 | $content .= "</div>"; |
| 5630 | 5815 | |
| 5816 | + return $this->special_page_html('', $content); | |
| 5817 | + } | |
| 5631 | 5818 | |
| 5632 | - $this->fakepage(__('Waiting for your order confirmation','woo-vipps'), $content); | |
| 5819 | + // Returns formatted html for the vipps special page. LP 2026-08-27 | |
| 5820 | + public function special_page_html($header, $content) { | |
| 5821 | + $header_html = $header ? "<h2 class='vipps-special-page-title page-title'>$header</h2>" : ''; | |
| 5822 | + $html = <<<EOF | |
| 5823 | + $header_html | |
| 5824 | + <div class="vipps-special-page-content">$content</div> | |
| 5825 | + EOF; | |
| 5826 | + return apply_filters('woo_vipps_special_page_html', $html, $header, $content); | |
| 5633 | 5827 | } |
| 5634 | 5828 | |
| 5635 | - | |
| 5636 | - | |
| 5637 | - public function fakepage($title,$content) { | |
| 5638 | - global $wp, $wp_query; | |
| 5639 | - // We don't want this here. | |
| 5640 | - remove_filter ('the_content', 'wpautop'); | |
| 5641 | - | |
| 5642 | - $specialid = $this->gateway()->get_option('vippsspecialpageid'); | |
| 5643 | - $wp_post = null; | |
| 5644 | - if ($specialid) { | |
| 5645 | - $wp_post = get_post($specialid); | |
| 5646 | - if ($wp_post) { | |
| 5647 | - $wp_post->post_title = $title; | |
| 5648 | - $wp_post->post_content = $content; | |
| 5649 | - // Normalize a bit | |
| 5650 | - $wp_post->filter = 'raw'; // important | |
| 5651 | - $wp_post->post_status = 'publish'; | |
| 5652 | - $wp_post->comment_status= 'closed'; | |
| 5653 | - $wp_post->ping_status= 'closed'; | |
| 5654 | - } else { | |
| 5655 | - $this->log(sprintf(__("Could not use special page with id %s - it seems not to exist.", 'woo-vipps'), $specialid), 'error'); | |
| 5656 | - } | |
| 5657 | - } | |
| 5658 | - if (!$wp_post || is_wp_error($wp_post)) { | |
| 5659 | - $post = new stdClass(); | |
| 5660 | - $post->ID = -99; | |
| 5661 | - $post->post_author = 1; | |
| 5662 | - $post->post_date = current_time( 'mysql' ); | |
| 5663 | - $post->post_date_gmt = current_time( 'mysql', 1 ); | |
| 5664 | - $post->post_title = $title; | |
| 5665 | - $post->post_content = $content; | |
| 5666 | - $post->post_status = 'publish'; | |
| 5667 | - $post->comment_status = 'closed'; | |
| 5668 | - $post->ping_status = 'closed'; | |
| 5669 | - $post->post_name = 'vippsconfirm-fake-page-name'; | |
| 5670 | - $post->post_type = 'page'; | |
| 5671 | - $post->filter = 'raw'; // important | |
| 5672 | - $wp_post = new WP_Post($post); | |
| 5673 | - wp_cache_add( -99, $wp_post, 'posts' ); | |
| 5674 | - } | |
| 5675 | - | |
| 5676 | - // Update the main query | |
| 5677 | - $wp_query->post = $wp_post; | |
| 5678 | - $wp_query->posts = array( $wp_post ); | |
| 5679 | - $wp_query->queried_object = $wp_post; | |
| 5680 | - $wp_query->queried_object_id = $wp_post->ID; | |
| 5681 | - $wp_query->found_posts = 1; | |
| 5682 | - $wp_query->post_count = 1; | |
| 5683 | - $wp_query->max_num_pages = 1; | |
| 5684 | - $wp_query->is_page = true; | |
| 5685 | - $wp_query->is_singular = true; | |
| 5686 | - $wp_query->is_single = false; | |
| 5687 | - $wp_query->is_attachment = false; | |
| 5688 | - $wp_query->is_archive = false; | |
| 5689 | - $wp_query->is_category = false; | |
| 5690 | - $wp_query->is_tag = false; | |
| 5691 | - $wp_query->is_tax = false; | |
| 5692 | - $wp_query->is_author = false; | |
| 5693 | - $wp_query->is_date = false; | |
| 5694 | - $wp_query->is_year = false; | |
| 5695 | - $wp_query->is_month = false; | |
| 5696 | - $wp_query->is_day = false; | |
| 5697 | - $wp_query->is_time = false; | |
| 5698 | - $wp_query->is_search = false; | |
| 5699 | - $wp_query->is_feed = false; | |
| 5700 | - $wp_query->is_comment_feed = false; | |
| 5701 | - $wp_query->is_trackback = false; | |
| 5702 | - $wp_query->is_home = false; | |
| 5703 | - $wp_query->is_embed = false; | |
| 5704 | - $wp_query->is_404 = false; | |
| 5705 | - $wp_query->is_paged = false; | |
| 5706 | - $wp_query->is_admin = false; | |
| 5707 | - $wp_query->is_preview = false; | |
| 5708 | - $wp_query->is_robots = false; | |
| 5709 | - $wp_query->is_posts_page = false; | |
| 5710 | - $wp_query->is_post_type_archive = false; | |
| 5711 | - // Update globals | |
| 5712 | - $GLOBALS['wp_query'] = $wp_query; | |
| 5713 | - $wp->register_globals(); | |
| 5714 | - return $wp_post; | |
| 5715 | - } | |
| 5716 | 5829 | |
| 5717 | 5830 | // Support the interactivity API with data about our cart IOK 2026-02-23 |
| 5718 | 5831 | public function woo_vipps_store_api_cart_data() { |
| 5719 | 5832 | // 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 |