PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.3.1
Pay with Vipps and MobilePay for WooCommerce v6.3.1
6.3.1 6.2.6 6.3.0 6.2.5 6.2.4 6.2.3 6.2.2 6.2.1 6.2.0 6.1.10 6.1.9 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1.0 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 All 190 releases
← All changes | payment/WC_Gateway_Vipps.class.php +73 -82 6.1.7 → 6.3.1 View file →
@@ -215,8 +215,26 @@
215 215 add_action('woocommerce_order_status_refunded', array($this, 'maybe_refund_order'), 9, 1);
216 216
217 217 // Possibly delete orders that never went anywhere
218 218 add_action('woocommerce_order_status_pending_to_cancelled', array($this, 'maybe_delete_order'), 99999, 1);
219 +
220 + // Disable emails for cancelled express orders that never went anywhere IOK 2026-09-09
221 + add_filter('woocommerce_email_enabled_cancelled_order', function ( $enabled, $order, $email ) {
222 + if ( ! $order instanceof WC_Order ) {
223 + return $enabled;
224 + }
225 + $pm = $order->get_payment_method();
226 + if (! Vipps::is_vipps_order($pm)){
227 + return $enabled;
228 + }
229 + $is_vipps_express = (bool) $order->get_meta( '_vipps_express_checkout' );
230 + $has_billing_email = (bool) $order->get_billing_email();
231 + if ( $is_vipps_express) {
232 + return false;
233 + }
234 + return $enabled;
235 + }, 10, 3);
236 +
219 237 // Handle orders when authorized
220 238 add_action('woocommerce_payment_complete', array($this, 'order_payment_complete'), 10, 1);
221 239
222 240 // when an order is complete, we need to check if there is reserved amount that is not captured
@@ -603,22 +621,8 @@
603 621 }
604 622 public function shipping_details_callback_url($token='',$reference=0) {
605 623 return $this->make_callback_urls('vipps_shipping_details',$token,$reference);
606 624 }
607 - // Callback for the consetn removal callback. Must use template redirect directly, because wc-api doesn't handle DELETE.
608 - // IOK 2018-05-18
609 - public function consent_removal_callback_url () {
610 - $queryargs = [];
611 - $url = home_url("/", 'https');
612 - if ( !get_option('permalink_structure')) {
613 - $queryargs['vipps-consent-removal']=1;
614 - } else {
615 - $url = trailingslashit(home_url('vipps-consent-removal', 'https'));
616 - }
617 - // And we need to add an empty "callback" query arg as the very last arg to receive the actual callback.
618 - // We can't use add_query_arg for that, as an empty argument will remove the equals-sign.
619 - return add_query_arg($queryargs, $url) . "&callback=";
620 - }
621 625
622 626 // Allow user to select the template to be used for the special Vipps MobilePay pages. IOK 2020-02-17
623 627 public function get_theme_page_templates() {
624 628 if (!$this->page_templates) {
@@ -630,22 +634,8 @@
630 634 }
631 635 return $this->page_templates;
632 636 }
633 637
634 - // We can't use get_pages to get a default list of pages for our settings, because it triggers
635 - // actions that can be used by other plugins. Therefore we must use the database directly and cache the results. IOK 2023-08-22
636 - public function get_pagelist () {
637 - if (!$this->page_list) {
638 - global $wpdb;
639 - $page_list = array(''=>__('Use a simulated page (default)', 'woo-vipps'));
640 - foreach($wpdb->get_results("SELECT ID,post_title FROM {$wpdb->prefix}posts WHERE post_type='page' and post_status='publish'") as $page) {
641 - $page_list[$page->ID] = $page->post_title;
642 - }
643 - $this->page_list = $page_list;
644 - }
645 - return $this->page_list;
646 - }
647 -
648 638 // Check to see if the product in question can be bought with express checkout IOK 2018-12-04
649 639 public function product_supports_express_checkout($product) {
650 640 // IOK 2023-12-12 Can only support express checkout for Vipps - not MobilePay (yet!)
651 641 // IOK 2025-09-01 Now supports mobilepay
@@ -797,9 +787,9 @@
797 787 $order->save();
798 788 }
799 789
800 790 // IOK 2024-09-01 In general, we can refund most Vipps Mobilepay orders through the api,
801 - // however, this is not the case for the Bank Transfer method available through Vipps Checkout.
791 + // however, this is not the case for the Bank Transfer method available through Checkout.
802 792 public function can_refund_order( $order ) {
803 793 $method = $order->get_meta('_vipps_api');
804 794 switch ($method) {
805 795 case 'banktransfer':
@@ -1055,9 +1045,8 @@
1055 1045 global $Vipps;
1056 1046
1057 1047 // Used for defaults in the admin interface; however this functions is called a loot more often than that.
1058 1048 $page_templates = $this->get_theme_page_templates();
1059 - $page_list = $this->get_pagelist();
1060 1049
1061 1050 $orderprefix = $Vipps->generate_order_prefix();
1062 1051
1063 1052 // Default handling based on other parameters and earlier values.
@@ -1068,16 +1057,15 @@
1068 1057 if (class_exists('VippsWooLogin')) {
1069 1058 $woodefault = 'yes' === get_option('woocommerce_enable_signup_and_login_from_checkout');
1070 1059 if ($woodefault) {
1071 1060 $expresscreateuserdefault = "yes";
1072 - // $vippscreateuserdefault = "yes"; // However, for Vipps Checkout the email address is freetext so we'll treat the default a bit different.
1061 + // $vippscreateuserdefault = "yes"; // However, for Checkout the email address is freetext so we'll treat the default a bit different.
1073 1062 }
1074 1063 }
1075 1064
1076 - // We will only show the Vipps Checkout options if the user has activated the feature (thus creating the pages involved etc). IOK 2021-10-01
1065 + // We will only show the Checkout options if the user has activated the feature (thus creating the pages involved etc). IOK 2021-10-01
1077 1066 $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false);
1078 1067
1079 -
1080 1068 // This is used for new options,to set reasonable defaults based on older settings. We can't use WC_Settings->get_option for this unfortunately.
1081 1069 $current = get_option('woocommerce_vipps_settings');
1082 1070 // New defaults based on old defaults
1083 1071 $default_static_shipping_for_checkout = 'no';
@@ -1240,9 +1228,9 @@
1240 1228 'description' => __('Your phone number where Porterbuddy may send you important messages. Format must be MSISDN (including country code). Example: "4791234567"','woo-vipps'),
1241 1229 'default' => '',
1242 1230 ),
1243 1231
1244 - // Vipps checkout *shipping options* - extra shipping options that only work with Vipps Checkout
1232 + // Vipps checkout *shipping options* - extra shipping options that only work with Checkout
1245 1233 'vcs_helthjem' => array(
1246 1234 'title' => __('Helthjem', 'woo-vipps'),
1247 1235 'label' => sprintf(__('Support Helthjem as a shipping method in %1$s', 'woo-vipps'), Vipps::CheckoutName()),
1248 1236 'type' => 'checkbox',
@@ -1277,9 +1265,9 @@
1277 1265 ),
1278 1266
1279 1267 );
1280 1268
1281 - /* Support for *certain* external payment methods in Vipps Checkout. IOK 2024-05-27 */
1269 + /* Support for *certain* external payment methods in Checkout. IOK 2024-05-27 */
1282 1270 $externals = [];
1283 1271 $external_payment_fields = [];
1284 1272 $allow_external_payments = $this->allow_external_payments_in_checkout();
1285 1273 if ($allow_external_payments) {
@@ -1602,22 +1590,24 @@
1602 1590 'description' => __('Turn this on to add support for Woos Order Attribution API for Checkout and Express Checkout. Some stores have reported problems when using this API together with Vipps, so be sure to test this if you turn it on.', 'woo-vipps'),
1603 1591 ),
1604 1592
1605 1593 'vippsspecialpagetemplate' => array(
1606 - 'title' => sprintf(__('Override page template used for the special %1$s pages', 'woo-vipps'), Vipps::CompanyName()),
1594 + 'title' => sprintf(__('Legacy: Override page template used for the special %1$s page', 'woo-vipps'), Vipps::CompanyName()),
1607 1595 'label' => sprintf(__('Use specific template for %1$s', 'woo-vipps'), Vipps::CompanyName()),
1608 1596 'type' => 'select',
1609 1597 'options' => $page_templates,
1610 - 'description' => sprintf(__('Use this template from your theme or child-theme to display all the special %1$s pages. You will probably want a full-width template and it should call \'the_content()\' normally.', 'woo-vipps'), Vipps::CompanyName()),
1598 + 'description' => sprintf(__('Use this template from your theme or child-theme for the special %1$s page.<br>Legacy: This is not necessary anymore - you should instead choose a template by editing the page like any other page.','woo-vipps'), Vipps::CompanyName()),
1611 1599 'default' => ''),
1612 1600
1601 + // Deprecated, not shown anymore: TODO: remove this option in future. LP 2026-09-01
1613 1602 'vippsspecialpageid' => array(
1614 1603 'title' => sprintf(__('Use a real page ID for the special %1$s pages - neccessary for some themes', 'woo-vipps'), Vipps::CompanyName()),
1615 1604 'label' => __('Use a real page ID', 'woo-vipps'),
1616 1605 'type' => 'select',
1617 - 'options' => $page_list,
1606 + 'options' => [],
1618 1607 'description' => sprintf(__('Some very few themes do not work with the simulated pages used by this plugin, and needs a real page ID for this. Choose a blank page for this; the content will be replaced, but the template and other metadata will be present. You only need to use this if the plugin seems to break on the special %1$s pages.', 'woo-vipps'), Vipps::CompanyName()),
1619 - 'default'=>''),
1608 + 'default' => ''
1609 + ),
1620 1610
1621 1611 'sendreceipts' => array(
1622 1612 'title' => __("Send receipts and order confirmation info to the customers' app on completed purchases.", 'woo-vipps'),
1623 1613 'label' => sprintf(__("Send receipts to the customers %1\$s app", 'woo-vipps'), Vipps::CompanyName()),
@@ -1633,17 +1623,8 @@
1633 1623 'description' => sprintf(__('If set, this image will be uploaded to %1$s and used to profile your store in the %1$s app for links to the order confirmation etc', 'woo-vipps'), Vipps::CompanyName()),
1634 1624 'default' => 0,
1635 1625 ),
1636 1626
1637 -
1638 - 'use_flock' => array (
1639 - 'title' => __('Use flock() to lock orders for Express Checkout', 'woo-vipps'),
1640 - 'label' => __('Use flock() to lock orders for Express Checkout', 'woo-vipps'),
1641 - 'type' => 'checkbox',
1642 - 'description' => __('Use the flock() system call to ensure orders are only finalized once. You can use this for normal setups, but probably not on Windows with IIS, and possibly not on distributed filesystems like NFS. If you don\t know what it is, probably do not use it. If you get duplicated shipping lines on some express orders, you may try using this', 'woo-vipps'),
1643 - 'default' => 'no',
1644 - ),
1645 -
1646 1627 'delete_settings_on_deactivation' => array (
1647 1628 'title' => __('Delete plugin settings on deactivation', 'woo-vipps'),
1648 1629 'label' => __('Delete plugin settings on deactivation', 'woo-vipps'),
1649 1630 'type' => 'checkbox',
@@ -1781,9 +1762,9 @@
1781 1762 $ok = apply_filters('woo_vipps_is_available', $ok, $this);
1782 1763 return $ok;
1783 1764 }
1784 1765
1785 - // True if the alternative Vipps Checkout screen is both available and activated. Returns the page id of the checkout
1766 + // True if the alternative Checkout screen is both available and activated. Returns the page id of the checkout
1786 1767 // page for convenience. IOK 2021-10-01
1787 1768 public function vipps_checkout_available () {
1788 1769
1789 1770 if ($this->get_option('vipps_checkout_enabled') != 'yes') return false;
@@ -1854,9 +1835,8 @@
1854 1835 wc_add_notice(sprintf(__('Unfortunately, the %1$s payment method is currently unavailable. Please choose another method.','woo-vipps'), $this->get_payment_method_name()),'error');
1855 1836 return [];
1856 1837 }
1857 1838
1858 -
1859 1839 // From the request, get either [billing_phone] => or [vipps phone]
1860 1840 $phone = '';
1861 1841 if (isset($_POST['vippsphone'])) {
1862 1842 $phone = trim(sanitize_text_field($_POST['vippsphone']));
@@ -1949,9 +1929,8 @@
1949 1929 $limited_session = $this->generate_authtoken();
1950 1930 $returnurl = add_query_arg('ls',$limited_session,$returnurl);
1951 1931 $returnurl = add_query_arg('id', $order_id, $returnurl);
1952 1932
1953 -
1954 1933 try {
1955 1934 // If the order was 'failed', it isnt any more! yet!
1956 1935 if ($order->get_status() == 'failed') {
1957 1936 $order->set_status('pending', __('Setting order status to pending to start payment', 'woo-vipps'));
@@ -1999,12 +1978,14 @@
1999 1978 $order->update_meta_data('_vipps_init_timestamp',$vippstamp);
2000 1979 $order->update_meta_data('_vipps_orderurl', $url);
2001 1980
2002 1981 $order->update_meta_data('_vipps_status','INITIATE'); // INITIATE right now
2003 - $order->add_order_note(sprintf(__('%1$s payment initiated','woo-vipps'), $this->get_payment_method_name()));
2004 - $order->add_order_note(sprintf(__('Awaiting %1$s payment confirmation','woo-vipps'), $this->get_payment_method_name()));
1982 +
1983 + $name = $this->get_payment_method_name();
1984 + $order->add_order_note(sprintf(__('%1$s payment initiated','woo-vipps'), $name));
1985 + $order->add_order_note(sprintf(__('Awaiting %1$s payment confirmation','woo-vipps'),$name));
1986 +
2005 1987 $order->save();
2006 -
2007 1988 // Create a signal file that we can check without calling wordpress to see if our result is in IOK 2018-05-04
2008 1989 try {
2009 1990 $Vipps->createCallbackSignal($order);
2010 1991 } catch (Exception $e) {
@@ -2009,9 +1990,8 @@
2009 1990 $Vipps->createCallbackSignal($order);
2010 1991 } catch (Exception $e) {
2011 1992 // Could not create a signal file, but that's ok.
2012 1993 }
2013 -
2014 1994 do_action('woo_vipps_before_redirect_to_vipps',$order_id);
2015 1995
2016 1996 // This will send us to a receipt page where we will do the actual work. IOK 2018-04-20
2017 1997 return array('result'=>'success','redirect'=>$url);
@@ -2367,15 +2347,15 @@
2367 2347 // Default should never happen, but just to ensure we are in our enumeration
2368 2348 return "initiated";
2369 2349 }
2370 2350
2371 - // This does not normally call Vipps, so if you need to refresh status, please use callback_check_order_status first. IOK 2019-01-23
2351 + // This does not normally call Vipps, so if you need to refresh status, please use poll_and_check_order_status first. IOK 2019-01-23
2372 2352 public function check_payment_status($order) {
2373 2353 if (!$order) return 'cancelled';
2374 2354 $status = $this->interpret_vipps_order_status($order->get_meta('_vipps_status'));
2375 2355 // This can happen if the vipps status is set from the back end for instance. IOK 2020-08-14
2376 2356 if ($order->get_status() == 'pending' && $status != 'initiated') {
2377 - $this->callback_check_order_status($order);
2357 + $this->poll_and_check_order_status($order);
2378 2358 $order = wc_get_order($order->get_id()); // refresh to get the new status IOK 2021-01-20
2379 2359 $status = $this->interpret_vipps_order_status($order->get_meta('_vipps_status'));
2380 2360 }
2381 2361 return $status;
@@ -2380,9 +2360,9 @@
2380 2360 }
2381 2361 return $status;
2382 2362 }
2383 2363
2384 - // Called by callback_check_order_status and handle_callback to handle the situation where
2364 + // Called by poll_and_check_order_status and handle_callback to handle the situation where
2385 2365 // the payment method has been set to something else *after* Vipps has gotten the order.
2386 2366 // This happens very rarely for people who use Vipps as an external payment method in Klarna, so
2387 2367 // we only do it for orders that match this. IOK 2023-02-03
2388 2368 public function reset_erroneous_payment_method($order) {
@@ -2409,9 +2389,9 @@
2409 2389 }
2410 2390
2411 2391 // Check status of order at Vipps, in case the callback has been delayed or failed.
2412 2392 // Should only be called if in status 'pending'; it will modify the order when status changes.
2413 - public function callback_check_order_status($order, $allow_retry = true) {
2393 + public function poll_and_check_order_status($order, $allow_retry = true) {
2414 2394 global $Vipps;
2415 2395 $orderid = $order->get_id();
2416 2396
2417 2397 clean_post_cache($order->get_id());
@@ -2444,15 +2424,8 @@
2444 2424 }
2445 2425 // Something changed, so we are now going to sideeffect the order. IOK 2025-10-15
2446 2426 $this->log(sprintf(__("%1\$s poll: Handling order: ", 'woo-vipps'), Vipps::CompanyName()) . " " . $orderid, 'debug');
2447 2427
2448 - // If we are in the process of getting a callback from vipps, don't update anything. Currently, Woo/WP has no locking mechanism,
2449 - // and it isn't feasible to implement one portably. So this reduces somewhat the likelihood of races when this method is called
2450 - // and callbacks happen at the same time.
2451 - if (!$Vipps->lockOrder($order)) {
2452 - return $oldstatus;
2453 - }
2454 -
2455 2428 // Failsafe for rare bug when using Klarna Checkout with Vipps as an external payment method
2456 2429 // IOK 2024-01-09 ensure this is called only when order is complete/authorized
2457 2430 if ($ready) {
2458 2431 $this->reset_erroneous_payment_method($order);
@@ -2484,9 +2457,8 @@
2484 2457
2485 2458 } catch (Exception $e) {
2486 2459 $this->log(sprintf(__("Error getting payment details from %1\$s for order_id:",'woo-vipps'), $this->get_payment_method_name()) . $orderid . "\n" . $e->getMessage(), 'error');
2487 2460 clean_post_cache($order->get_id());
2488 - $Vipps->unlockOrder($order);
2489 2461 return $oldstatus;
2490 2462 }
2491 2463 $order->save();
2492 2464
@@ -2519,9 +2491,8 @@
2519 2491 $this->log(sprintf(__("No address information for order %2\$d, but there still might be an active %1\$s session for it, so do not cancel it.", 'woo-vipps'), Vipps::CheckoutName(), $order->get_id()));
2520 2492 }
2521 2493 }
2522 2494 clean_post_cache($order->get_id());
2523 - $Vipps->unlockOrder($order);
2524 2495 return $oldstatus;
2525 2496 }
2526 2497 }
2527 2498
@@ -2557,9 +2528,8 @@
2557 2528
2558 2529 $order->save();
2559 2530 clean_post_cache($order->get_id());
2560 2531 $newstatus = $order->get_status();
2561 - $Vipps->unlockOrder($order);
2562 2532 return $newstatus;
2563 2533 }
2564 2534
2565 2535 // IOK 2020-01-20 Previously was just a debugging tool, then was used to update postmeta values. Now is used as the main source of info
@@ -2676,9 +2646,9 @@
2676 2646 $ready = true;
2677 2647 }
2678 2648
2679 2649 if ($ready && ($express || $checkout_session)) {
2680 - // For Vipps Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10
2650 + // For Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10
2681 2651 // This will also normalize userDetails, adding 'sub' where possible and fields for backwards compatibility. 2025-08-12
2682 2652 $result = $this->ensure_userDetails($result, $order);
2683 2653
2684 2654 // After, we need to normalize shipping details or even add them if e.g. using Checkout without address or contact info IOK 2025-08-13
@@ -2751,9 +2721,9 @@
2751 2721 return $result;
2752 2722 }
2753 2723
2754 2724
2755 - // IOK 2024-01-09 If using Vipps Checkout with the BankTransfer method, which is eg. used in Finland,
2725 + // IOK 2024-01-09 If using Checkout with the BankTransfer method, which is eg. used in Finland,
2756 2726 // we are (currently) not receiving any 'state' or 'aggregate', so add this iff the payment is successful.
2757 2727 // The reason for this is that this payment type does not actually use the epayment API at all (!)
2758 2728 // Also moved some other compatibility code here -
2759 2729 // --- reference used to be orderId
@@ -2822,9 +2792,9 @@
2822 2792
2823 2793 return $result;
2824 2794 }
2825 2795
2826 - // Vipps Checkout v3 does *not* provide userDetails. Vipps Checkout v2 and epayment *does*. But Checkout additionally allows
2796 + // Checkout v3 does *not* provide userDetails. Checkout v2 and epayment *does*. But Checkout additionally allows
2827 2797 // for anonymous purchases, in which case there is *no* user details. In this case we provide an anonymous user so we can actually create an order.
2828 2798 // To handle this, we provide this utility that ensures we have userDetails no matter the input. For this we use the anonymous filters and "billingDetails" if present
2829 2799 // if not, we use shippingDetails. IOK 2023-01-10
2830 2800 // Also, epayment uses mobileNumber and checkout uses phoneNumber, so normalize.
@@ -3201,9 +3171,14 @@
3201 3171 $is_base64 = $shipping_table ? ( $shipping_table['_is_base64'] ?? false) : false;
3202 3172
3203 3173 if (is_array($shipping_table) && isset($shipping_table[$key])) {
3204 3174 $decoded = $is_base64 ? @base64_decode($shipping_table[$key]) : $shipping_table[$key];
3205 - $shipping_rate = $decoded ? @unserialize($decoded) : null;
3175 +
3176 + // Ensure no shop manager has injected an evil object (that they would have had to add as a plugin) here. IOK 2026-09-18
3177 + $allowed_classes = apply_filters('woo_vipps_express_checkout_allowed_shipping_classes', [WC_Shipping_Rate::class, \stdClass::class]);
3178 + $shipping_rate = $decoded ? @unserialize($decoded, ['allowed_classes' => $allowed_classes]) : null;
3179 + $shipping_rate = is_a($shipping_rate,'WC_Shipping_Rate') ? $shipping_rate : null;
3180 +
3206 3181 if (!$shipping_rate) {
3207 3182 $this->log(sprintf(__("%1\$s: Could not deserialize the chosen shipping method %2\$s for order %3\$d", 'woo-vipps'), Vipps::ExpressCheckoutName(), $method, $order->get_id()), 'error');
3208 3183 $this->log(sprintf(__("Serialized data was %1\$s", 'woo-vipps'), $decoded), 'error');
3209 3184 } else {
@@ -3223,9 +3198,9 @@
3223 3198 }
3224 3199 }
3225 3200 }
3226 3201
3227 - // Possible extra metadata from Vipps Checkout IOK 2023-01-17
3202 + // Possible extra metadata from Checkout IOK 2023-01-17
3228 3203 // Store in the order, but also in the shipping rate so it will be visible in the order screen
3229 3204 // along with the shipping ragte
3230 3205 if (isset($shipping['pickupPoint'])) {
3231 3206 $order->update_meta_data('vipps_checkout_pickupPoint', $shipping['pickupPoint']);
@@ -3285,9 +3260,9 @@
3285 3260 $methodclass = $methods_classes[$shipping_rate->get_method_id()] ?? null;
3286 3261 $shipping_method = $methodclass ? new $methodclass($shipping_rate->get_instance_id()) : null;
3287 3262 $is_vipps_checkout_shipping = $shipping_method && is_a($shipping_method, 'VippsCheckout_Shipping_Method');
3288 3263
3289 - // Some Vipps Checkout-specific shipping methods calculate the cost in the Vipps window.
3264 + // Some Checkout-specific shipping methods calculate the cost in the Vipps window.
3290 3265 if ($is_vipps_checkout_shipping && $shipping_method->dynamic_cost) {
3291 3266 $vippsamount = intval($order->get_meta('_vipps_amount'));
3292 3267 $shipping_tax_rate = floatval($order->get_meta('_vipps_shipping_tax_rates'));
3293 3268 $compareamount = $ordertotal * 100;
@@ -3323,9 +3298,9 @@
3323 3298
3324 3299 $order->set_total($ordertotal + $total_shipping + $total_shipping_tax);
3325 3300 $order->update_taxes(); // Necessary for the admin view only; does not recalculate order.
3326 3301
3327 - // Add an early hook for Vipps Checkout orders with special shipping methods
3302 + // Add an early hook for Checkout orders with special shipping methods
3328 3303 $metadata = $shipping_rate->get_meta_data();
3329 3304 if (isset($metadata['type'])) {
3330 3305 do_action('woo_vipps_checkout_special_shipping_method', $order, $shipping_rate, $metadata['type']);
3331 3306 }
@@ -3341,9 +3316,9 @@
3341 3316
3342 3317
3343 3318 // If we have the 'expresscreateuser' thing set to true, we will create or assign the order here, as it is the first-ish place where we can.
3344 3319 // If possible and safe, user will be logged in before being sent to the thankyou screen. IOK 2020-10-09
3345 - // Same thing for Vipps Checkout, mutatis mutandis. The function below returns false if no customer exists or gets created.
3320 + // Same thing for Checkout, mutatis mutandis. The function below returns false if no customer exists or gets created.
3346 3321 $customer = false;
3347 3322 if ($assigncustomer) {
3348 3323 $customer = Vipps::instance()->express_checkout_get_vipps_customer($order);
3349 3324 }
@@ -3387,9 +3362,9 @@
3387 3362 $shipping_rate = apply_filters('woo_vipps_express_checkout_shipping_rate',$shipping_rate,$costExTax,$tax,$method,$product);
3388 3363 return $shipping_rate;
3389 3364 }
3390 3365
3391 - // Used by both callback_check_order_status and handle_callback - sets the neccessary order metadata after a successful (or not vipps transaction). IOK 2025-08-13
3366 + // Used by both poll_and_check_order_status and handle_callback - sets the neccessary order metadata after a successful (or not vipps transaction). IOK 2025-08-13
3392 3367 public function order_set_transaction_metadata($order, $transaction) {
3393 3368 // Set Vipps metadata as early as possible
3394 3369 $vippsstamp = strtotime($transaction['timeStamp']);
3395 3370 $vippsamount = $transaction['amount'] ?? '';
@@ -3676,9 +3651,9 @@
3676 3651 // made to the session here will be saved. Sorry.
3677 3652 // UPDATE: Should be no more race condition since we moved callback into the action scheduler, and this shipping finalization into this rest endpoint. LP 2026-03-30
3678 3653 Vipps::instance()->callback_restore_session($order_id);
3679 3654
3680 - // For Vipps Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10
3655 + // For Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10
3681 3656 // This will also normalize userDetails, adding 'sub' where possible and fields for backwards compatibility. 2025-08-12
3682 3657 $data = $this->ensure_userDetails($data, $order);
3683 3658
3684 3659 // Some Express Checkout orders aren't really express checkout orders, but normal orders to which we have
@@ -3769,9 +3744,9 @@
3769 3744 }
3770 3745 do_action('woo_vipps_payment_complete_at_shutdown', $order, $this);
3771 3746 } catch (Exception $e) {
3772 3747 // This is/should be non-critical so just log it.
3773 - $this->log(sprintf(__("Could not do all payment-complete actions on %1\$s order %2\$d: %3\$s ", 'woo-vipps'), Vipps::CompanyName(), $orderid, $e->etMessage()), "error");
3748 + $this->log(sprintf(__("Could not do all payment-complete actions on %1\$s order %2\$d: %3\$s ", 'woo-vipps'), Vipps::CompanyName(), $orderid, $e->getMessage()), "error");
3774 3749 }
3775 3750 }
3776 3751
3777 3752 // This is run on payment complete. Per default will it only add a link to the order confirmation page, but
@@ -3878,9 +3853,9 @@
3878 3853
3879 3854 $contents = WC()->cart->get_cart_contents();
3880 3855 $contents = apply_filters('woo_vipps_create_express_checkout_cart_contents',$contents);
3881 3856 try {
3882 - $cart_hash = md5(json_encode(wc_clean($contents)) . WC()->cart->total);
3857 + $cart_hash = WC()->cart->get_cart_hash();
3883 3858 $order = new WC_Order();
3884 3859 $order->set_status('pending');
3885 3860 $order->set_payment_method($this);
3886 3861 if ($ischeckout) {
@@ -3891,8 +3866,9 @@
3891 3866 }
3892 3867 // We use 'checkout' as the created_via key as per requests, but allow merchants to use their own. IOK 2022-09-15
3893 3868 $created_via = apply_filters('woo_vipps_express_checkout_created_via', 'checkout', $order, $ischeckout);
3894 3869 $order->set_created_via($created_via);
3870 + $order->set_cart_hash($cart_hash);
3895 3871
3896 3872 $dummy = sprintf(__('Vipps Express Checkout', 'woo-vipps')); // this is so gettext will find this string.
3897 3873 $dummy = sprintf(__('Vipps Checkout', 'woo-vipps')); // this is so gettext will find this string.
3898 3874
@@ -4054,9 +4030,9 @@
4054 4030 </p>
4055 4031 </div>
4056 4032 <?php endif; ?>
4057 4033
4058 - <?php // We will only show the Vipps Checkout options if the user has activated the feature (thus creating the pages involved etc). IOK 2021-10-01
4034 + <?php // We will only show the Checkout options if the user has activated the feature (thus creating the pages involved etc). IOK 2021-10-01
4059 4035 $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false);
4060 4036 ?>
4061 4037
4062 4038 <?php /* We will *not* allow vipps checkout to be activated at this point, since the product is no longer sold. IOK 2026-04-30 */ ?>
@@ -4117,8 +4093,23 @@
4117 4093 // If enabling this, ensure the page in question exists
4118 4094 if ($this->get_option('vipps_checkout_enabled') == 'yes') {
4119 4095 update_option('woo_vipps_checkout_activated', true, true); // This must be true here, but still, make sure
4120 4096 Vipps::instance()->maybe_create_vipps_pages();
4097 + }
4098 +
4099 + // Ensure special page has the necessary shortcode. LP 2026-09-01
4100 + $special_page = get_post(Vipps::get_special_page_id());
4101 + if ($special_page && !has_shortcode($special_page->post_content, 'vipps_special_page')) {
4102 + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->";
4103 + wp_update_post([
4104 + 'ID' => Vipps::get_special_page_id(),
4105 + 'post_content' => $new_content,
4106 + ]);
4107 + } else if (!Vipps::get_special_page_id()) {
4108 + // We shouldn't really get here, the page should be ensured to exist in init. LP 2026-09-03
4109 + /* translators: %s is current method name */
4110 + $this->log(sprintf(__('Missing special page in %s, attempting to fix', 'woo-vipps'), 'process_admin_options'), 'warning');
4111 + Vipps::instance()->ensure_special_page_exists();
4121 4112 }
4122 4113
4123 4114 return $saved;
4124 4115 }