| @@ -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 && ! $has_billing_email ) { | |
| 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 |
| @@ -280,10 +298,8 @@ | ||
| 280 | 298 | public function maybe_cancel_reserved_amount ($orderid) { |
| 281 | 299 | $order = wc_get_order($orderid); |
| 282 | 300 | if (!$order) return; |
| 283 | 301 | if (! Vipps::is_vipps_order($order)) return false; |
| 284 | - // Cannot partially cancel legacy ecom orders | |
| 285 | - if ('epayment' != $order->get_meta('_vipps_api')) return false; | |
| 286 | 302 | |
| 287 | 303 | // Check that the normal maybe_capture_order hook has actually ran *and* done something, |
| 288 | 304 | // it's only after this we know we have captured 'everything' so if there is anything left, |
| 289 | 305 | // it should be cancelled. IOK 2025-05-04 |
| @@ -585,9 +601,9 @@ | ||
| 585 | 601 | // Webhook callbacks do not pass GET arguments at all, but do provide an X-Vipps-Authorization header for verification. IOK 2023-12-19 |
| 586 | 602 | public function webhook_callback_url () { |
| 587 | 603 | $url = home_url("/", 'https'); |
| 588 | 604 | $queryargs = ['callback'=>'webhook']; |
| 589 | - $forwhat = 'wc_gateway_vipps'; // Same callback as for ecom, checkout, express checkout | |
| 605 | + $forwhat = 'wc_gateway_vipps'; // Same callback as for epayment, checkout, express checkout | |
| 590 | 606 | // HTTPS required. IOK 2018-05-18 |
| 591 | 607 | // If the user for some reason hasn't enabled pretty links, fall back to ancient version. IOK 2018-04-24 |
| 592 | 608 | if ( !get_option('permalink_structure')) { |
| 593 | 609 | $queryargs['wc-api'] = $forwhat; |
| @@ -605,22 +621,8 @@ | ||
| 605 | 621 | } |
| 606 | 622 | public function shipping_details_callback_url($token='',$reference=0) { |
| 607 | 623 | return $this->make_callback_urls('vipps_shipping_details',$token,$reference); |
| 608 | 624 | } |
| 609 | - // Callback for the consetn removal callback. Must use template redirect directly, because wc-api doesn't handle DELETE. | |
| 610 | - // IOK 2018-05-18 | |
| 611 | - public function consent_removal_callback_url () { | |
| 612 | - $queryargs = []; | |
| 613 | - $url = home_url("/", 'https'); | |
| 614 | - if ( !get_option('permalink_structure')) { | |
| 615 | - $queryargs['vipps-consent-removal']=1; | |
| 616 | - } else { | |
| 617 | - $url = trailingslashit(home_url('vipps-consent-removal', 'https')); | |
| 618 | - } | |
| 619 | - // And we need to add an empty "callback" query arg as the very last arg to receive the actual callback. | |
| 620 | - // We can't use add_query_arg for that, as an empty argument will remove the equals-sign. | |
| 621 | - return add_query_arg($queryargs, $url) . "&callback="; | |
| 622 | - } | |
| 623 | 625 | |
| 624 | 626 | // Allow user to select the template to be used for the special Vipps MobilePay pages. IOK 2020-02-17 |
| 625 | 627 | public function get_theme_page_templates() { |
| 626 | 628 | if (!$this->page_templates) { |
| @@ -632,22 +634,8 @@ | ||
| 632 | 634 | } |
| 633 | 635 | return $this->page_templates; |
| 634 | 636 | } |
| 635 | 637 | |
| 636 | - // We can't use get_pages to get a default list of pages for our settings, because it triggers | |
| 637 | - // actions that can be used by other plugins. Therefore we must use the database directly and cache the results. IOK 2023-08-22 | |
| 638 | - public function get_pagelist () { | |
| 639 | - if (!$this->page_list) { | |
| 640 | - global $wpdb; | |
| 641 | - $page_list = array(''=>__('Use a simulated page (default)', 'woo-vipps')); | |
| 642 | - foreach($wpdb->get_results("SELECT ID,post_title FROM {$wpdb->prefix}posts WHERE post_type='page' and post_status='publish'") as $page) { | |
| 643 | - $page_list[$page->ID] = $page->post_title; | |
| 644 | - } | |
| 645 | - $this->page_list = $page_list; | |
| 646 | - } | |
| 647 | - return $this->page_list; | |
| 648 | - } | |
| 649 | - | |
| 650 | 638 | // Check to see if the product in question can be bought with express checkout IOK 2018-12-04 |
| 651 | 639 | public function product_supports_express_checkout($product) { |
| 652 | 640 | // IOK 2023-12-12 Can only support express checkout for Vipps - not MobilePay (yet!) |
| 653 | 641 | // IOK 2025-09-01 Now supports mobilepay |
| @@ -799,9 +787,9 @@ | ||
| 799 | 787 | $order->save(); |
| 800 | 788 | } |
| 801 | 789 | |
| 802 | 790 | // IOK 2024-09-01 In general, we can refund most Vipps Mobilepay orders through the api, |
| 803 | - // 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. | |
| 804 | 792 | public function can_refund_order( $order ) { |
| 805 | 793 | $method = $order->get_meta('_vipps_api'); |
| 806 | 794 | switch ($method) { |
| 807 | 795 | case 'banktransfer': |
| @@ -809,9 +797,9 @@ | ||
| 809 | 797 | break; |
| 810 | 798 | case 'epayment': |
| 811 | 799 | return true; |
| 812 | 800 | break; |
| 813 | - // Default is old-style ecom v2. | |
| 801 | + // Default is true; but the above are exhaustive IOK 2026-08-18 | |
| 814 | 802 | default: |
| 815 | 803 | return true; |
| 816 | 804 | break; |
| 817 | 805 | } |
| @@ -1057,9 +1045,8 @@ | ||
| 1057 | 1045 | global $Vipps; |
| 1058 | 1046 | |
| 1059 | 1047 | // Used for defaults in the admin interface; however this functions is called a loot more often than that. |
| 1060 | 1048 | $page_templates = $this->get_theme_page_templates(); |
| 1061 | - $page_list = $this->get_pagelist(); | |
| 1062 | 1049 | |
| 1063 | 1050 | $orderprefix = $Vipps->generate_order_prefix(); |
| 1064 | 1051 | |
| 1065 | 1052 | // Default handling based on other parameters and earlier values. |
| @@ -1070,16 +1057,15 @@ | ||
| 1070 | 1057 | if (class_exists('VippsWooLogin')) { |
| 1071 | 1058 | $woodefault = 'yes' === get_option('woocommerce_enable_signup_and_login_from_checkout'); |
| 1072 | 1059 | if ($woodefault) { |
| 1073 | 1060 | $expresscreateuserdefault = "yes"; |
| 1074 | - // $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. | |
| 1075 | 1062 | } |
| 1076 | 1063 | } |
| 1077 | 1064 | |
| 1078 | - // 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 | |
| 1079 | 1066 | $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false); |
| 1080 | 1067 | |
| 1081 | - | |
| 1082 | 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. |
| 1083 | 1069 | $current = get_option('woocommerce_vipps_settings'); |
| 1084 | 1070 | // New defaults based on old defaults |
| 1085 | 1071 | $default_static_shipping_for_checkout = 'no'; |
| @@ -1242,9 +1228,9 @@ | ||
| 1242 | 1228 | 'description' => __('Your phone number where Porterbuddy may send you important messages. Format must be MSISDN (including country code). Example: "4791234567"','woo-vipps'), |
| 1243 | 1229 | 'default' => '', |
| 1244 | 1230 | ), |
| 1245 | 1231 | |
| 1246 | - // 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 | |
| 1247 | 1233 | 'vcs_helthjem' => array( |
| 1248 | 1234 | 'title' => __('Helthjem', 'woo-vipps'), |
| 1249 | 1235 | 'label' => sprintf(__('Support Helthjem as a shipping method in %1$s', 'woo-vipps'), Vipps::CheckoutName()), |
| 1250 | 1236 | 'type' => 'checkbox', |
| @@ -1279,9 +1265,9 @@ | ||
| 1279 | 1265 | ), |
| 1280 | 1266 | |
| 1281 | 1267 | ); |
| 1282 | 1268 | |
| 1283 | - /* 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 */ | |
| 1284 | 1270 | $externals = []; |
| 1285 | 1271 | $external_payment_fields = []; |
| 1286 | 1272 | $allow_external_payments = $this->allow_external_payments_in_checkout(); |
| 1287 | 1273 | if ($allow_external_payments) { |
| @@ -1604,22 +1590,24 @@ | ||
| 1604 | 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'), |
| 1605 | 1591 | ), |
| 1606 | 1592 | |
| 1607 | 1593 | 'vippsspecialpagetemplate' => array( |
| 1608 | - '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()), | |
| 1609 | 1595 | 'label' => sprintf(__('Use specific template for %1$s', 'woo-vipps'), Vipps::CompanyName()), |
| 1610 | 1596 | 'type' => 'select', |
| 1611 | 1597 | 'options' => $page_templates, |
| 1612 | - '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()), | |
| 1613 | 1599 | 'default' => ''), |
| 1614 | 1600 | |
| 1601 | + // Deprecated, not shown anymore: TODO: remove this option in future. LP 2026-09-01 | |
| 1615 | 1602 | 'vippsspecialpageid' => array( |
| 1616 | 1603 | 'title' => sprintf(__('Use a real page ID for the special %1$s pages - neccessary for some themes', 'woo-vipps'), Vipps::CompanyName()), |
| 1617 | 1604 | 'label' => __('Use a real page ID', 'woo-vipps'), |
| 1618 | 1605 | 'type' => 'select', |
| 1619 | - 'options' => $page_list, | |
| 1606 | + 'options' => [], | |
| 1620 | 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()), |
| 1621 | - 'default'=>''), | |
| 1608 | + 'default' => '' | |
| 1609 | + ), | |
| 1622 | 1610 | |
| 1623 | 1611 | 'sendreceipts' => array( |
| 1624 | 1612 | 'title' => __("Send receipts and order confirmation info to the customers' app on completed purchases.", 'woo-vipps'), |
| 1625 | 1613 | 'label' => sprintf(__("Send receipts to the customers %1\$s app", 'woo-vipps'), Vipps::CompanyName()), |
| @@ -1783,9 +1771,9 @@ | ||
| 1783 | 1771 | $ok = apply_filters('woo_vipps_is_available', $ok, $this); |
| 1784 | 1772 | return $ok; |
| 1785 | 1773 | } |
| 1786 | 1774 | |
| 1787 | - // True if the alternative Vipps Checkout screen is both available and activated. Returns the page id of the checkout | |
| 1775 | + // True if the alternative Checkout screen is both available and activated. Returns the page id of the checkout | |
| 1788 | 1776 | // page for convenience. IOK 2021-10-01 |
| 1789 | 1777 | public function vipps_checkout_available () { |
| 1790 | 1778 | |
| 1791 | 1779 | if ($this->get_option('vipps_checkout_enabled') != 'yes') return false; |
| @@ -1856,9 +1844,8 @@ | ||
| 1856 | 1844 | 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'); |
| 1857 | 1845 | return []; |
| 1858 | 1846 | } |
| 1859 | 1847 | |
| 1860 | - | |
| 1861 | 1848 | // From the request, get either [billing_phone] => or [vipps phone] |
| 1862 | 1849 | $phone = ''; |
| 1863 | 1850 | if (isset($_POST['vippsphone'])) { |
| 1864 | 1851 | $phone = trim(sanitize_text_field($_POST['vippsphone'])); |
| @@ -1951,9 +1938,8 @@ | ||
| 1951 | 1938 | $limited_session = $this->generate_authtoken(); |
| 1952 | 1939 | $returnurl = add_query_arg('ls',$limited_session,$returnurl); |
| 1953 | 1940 | $returnurl = add_query_arg('id', $order_id, $returnurl); |
| 1954 | 1941 | |
| 1955 | - | |
| 1956 | 1942 | try { |
| 1957 | 1943 | // If the order was 'failed', it isnt any more! yet! |
| 1958 | 1944 | if ($order->get_status() == 'failed') { |
| 1959 | 1945 | $order->set_status('pending', __('Setting order status to pending to start payment', 'woo-vipps')); |
| @@ -2001,12 +1987,14 @@ | ||
| 2001 | 1987 | $order->update_meta_data('_vipps_init_timestamp',$vippstamp); |
| 2002 | 1988 | $order->update_meta_data('_vipps_orderurl', $url); |
| 2003 | 1989 | |
| 2004 | 1990 | $order->update_meta_data('_vipps_status','INITIATE'); // INITIATE right now |
| 2005 | - $order->add_order_note(sprintf(__('%1$s payment initiated','woo-vipps'), $this->get_payment_method_name())); | |
| 2006 | - $order->add_order_note(sprintf(__('Awaiting %1$s payment confirmation','woo-vipps'), $this->get_payment_method_name())); | |
| 1991 | + | |
| 1992 | + $name = $this->get_payment_method_name(); | |
| 1993 | + $order->add_order_note(sprintf(__('%1$s payment initiated','woo-vipps'), $name)); | |
| 1994 | + $order->add_order_note(sprintf(__('Awaiting %1$s payment confirmation','woo-vipps'),$name)); | |
| 1995 | + | |
| 2007 | 1996 | $order->save(); |
| 2008 | - | |
| 2009 | 1997 | // Create a signal file that we can check without calling wordpress to see if our result is in IOK 2018-05-04 |
| 2010 | 1998 | try { |
| 2011 | 1999 | $Vipps->createCallbackSignal($order); |
| 2012 | 2000 | } catch (Exception $e) { |
| @@ -2011,9 +1999,8 @@ | ||
| 2011 | 1999 | $Vipps->createCallbackSignal($order); |
| 2012 | 2000 | } catch (Exception $e) { |
| 2013 | 2001 | // Could not create a signal file, but that's ok. |
| 2014 | 2002 | } |
| 2015 | - | |
| 2016 | 2003 | do_action('woo_vipps_before_redirect_to_vipps',$order_id); |
| 2017 | 2004 | |
| 2018 | 2005 | // This will send us to a receipt page where we will do the actual work. IOK 2018-04-20 |
| 2019 | 2006 | return array('result'=>'success','redirect'=>$url); |
| @@ -2142,12 +2129,11 @@ | ||
| 2142 | 2129 | if ($api == 'banktransfer') { |
| 2143 | 2130 | // This is an error - we should not ever get to the 'capture' branch if we are a banktransfer payment. |
| 2144 | 2131 | // IOK 2024-01-09 |
| 2145 | 2132 | $content = []; |
| 2146 | - } elseif ($api == 'epayment') { | |
| 2133 | + } else { | |
| 2134 | + // Now the only other api is 'epayment' IOK 2026-08-18 | |
| 2147 | 2135 | $content = $this->api->epayment_capture_payment($order,$amount,$requestid); |
| 2148 | - } else { | |
| 2149 | - $content = $this->api->capture_payment($order,$amount,$requestid); | |
| 2150 | 2136 | } |
| 2151 | 2137 | } catch (TemporaryVippsApiException $e) { |
| 2152 | 2138 | $this->log(sprintf(__('Could not capture %1$s payment for order id:', 'woo-vipps'), $this->get_payment_method_name()) . ' ' . $order->get_id() . "\n" .$e->getMessage(),'error'); |
| 2153 | 2139 | $this->adminerr(sprintf(__('%1$s is temporarily unavailable.','woo-vipps'), $this->get_payment_method_name()) . "\n" . $e->getMessage()); |
| @@ -2198,8 +2184,9 @@ | ||
| 2198 | 2184 | return false; |
| 2199 | 2185 | } |
| 2200 | 2186 | // We'll use the same transaction id for all cancel jobs, as we can only do it completely. IOK 2018-05-07 |
| 2201 | 2187 | // For epayment, partial cancellations will be possible. IOK 2022-11-12 |
| 2188 | + // IOK 2026-08-18 actually, epayment does *not* support partial cancellation - all remaining funds are cancelled. | |
| 2202 | 2189 | $api = $order->get_meta('_vipps_api'); |
| 2203 | 2190 | try { |
| 2204 | 2191 | $requestid = ""; |
| 2205 | 2192 | if ($api == 'banktransfer') { |
| @@ -2204,22 +2191,14 @@ | ||
| 2204 | 2191 | $requestid = ""; |
| 2205 | 2192 | if ($api == 'banktransfer') { |
| 2206 | 2193 | // If we are here, and the order is somehow not captured, just do nothing. IOK 2024-01-09 |
| 2207 | 2194 | $content = []; |
| 2208 | - } elseif ($api == 'epayment') { | |
| 2195 | + } else { | |
| 2196 | + // api is here 'epayment'. IOK 2026-07-18 | |
| 2209 | 2197 | $requestid = 1; |
| 2210 | 2198 | // This will cancel any remaining, not-captured amount IOK 2026-01-28 |
| 2211 | 2199 | $content = $this->api->epayment_cancel_payment($order,$requestid); |
| 2212 | - } else { | |
| 2213 | - // If we have captured the order, we can't cancel it with the ecom API IOK 2018-05-07 | |
| 2214 | - $captured = intval($order->get_meta('_vipps_captured')); | |
| 2215 | - if ($captured>0) { | |
| 2216 | - $msg = sprintf(__('Cannot cancel a captured %1$s transaction - use refund instead', 'woo-vipps'), "ECOM " . $this->get_payment_method_name()); | |
| 2217 | - $this->adminerr($msg); | |
| 2218 | - return false; | |
| 2219 | - } | |
| 2220 | - $content = $this->api->cancel_payment($order,$requestid); | |
| 2221 | - } | |
| 2200 | + } | |
| 2222 | 2201 | } catch (TemporaryVippsApiException $e) { |
| 2223 | 2202 | $this->log(sprintf(__('Could not cancel %1$s payment for order_id:', 'woo-vipps'), $this->get_payment_method_name()) . ' ' . $order->get_id() . "\n" .$e->getMessage(),'error'); |
| 2224 | 2203 | $this->adminerr(sprintf(__('%1$s is temporarily unavailable.','woo-vipps'), $this->get_payment_method_name()) . ' ' . $e->getMessage()); |
| 2225 | 2204 | return false; |
| @@ -2233,9 +2212,8 @@ | ||
| 2233 | 2212 | // the epay v2 API would return transactionInfo and Summary with the result, the new epayment api returns nothing. |
| 2234 | 2213 | // Removed epay branch 2025-08-12 IOK |
| 2235 | 2214 | $total = intval($order->get_meta('_vipps_amount')); |
| 2236 | 2215 | $captured = intval($order->get_meta('_vipps_captured')); |
| 2237 | -# $cancelled = $amount + intval($order->get_meta('_vipps_cancelled'); | |
| 2238 | 2216 | $cancelled = $total; |
| 2239 | 2217 | $remaining = $total - $captured - $cancelled; |
| 2240 | 2218 | |
| 2241 | 2219 | // We need to assume it worked. Also, we can't do partial cancels yet, so just cancel everything. |
| @@ -2283,13 +2261,12 @@ | ||
| 2283 | 2261 | if ($api == 'banktransfer') { |
| 2284 | 2262 | $msg = sprintf(__("Cannot refund bank transfer order %1\$d", 'woo-vipps'), $order->get_id()); |
| 2285 | 2263 | $this->log($msg, 'error'); |
| 2286 | 2264 | throw new Exception($msg); |
| 2287 | - } elseif ($api == 'epayment') { | |
| 2265 | + } else { | |
| 2266 | + // api is now 'epayment' IOK 2026-08-18 | |
| 2288 | 2267 | $content = $this->api->epayment_refund_payment($order,$requestid,$amount,$cents); |
| 2289 | - } else { | |
| 2290 | - $content = $this->api->refund_payment($order,$requestid,$amount,$cents); | |
| 2291 | - } | |
| 2268 | + } | |
| 2292 | 2269 | |
| 2293 | 2270 | $currency = $order->get_currency(); |
| 2294 | 2271 | |
| 2295 | 2272 | // Previously, we got updated transaction info in a transactionInfo field. this is no longer provided, |
| @@ -2473,10 +2450,8 @@ | ||
| 2473 | 2450 | $order->update_meta_data('_vipps_status',$newvippsstatus); |
| 2474 | 2451 | |
| 2475 | 2452 | // Extract order metadata from either Checkout or Epayment - set below IOK 2025-08-13 |
| 2476 | 2453 | if (!empty($paymentdetails)) { |
| 2477 | - | |
| 2478 | - | |
| 2479 | 2454 | // checkout has a string, epayment has an array with upper case "type" and apparently, cardBin IOK 2025-08-12 |
| 2480 | 2455 | $paymentMethod = $paymentdetails['paymentMethod'] ?? "epayment"; |
| 2481 | 2456 | // After normalization, all APIs will have data here. |
| 2482 | 2457 | $details = $paymentdetails['paymentDetails']; |
| @@ -2689,73 +2664,11 @@ | ||
| 2689 | 2664 | if (in_array($newstatus, ['authorized', 'complete'])) { |
| 2690 | 2665 | $ready = true; |
| 2691 | 2666 | } |
| 2692 | 2667 | |
| 2693 | - | |
| 2694 | - // if this is *express - not checkout * and there is no user information, this is probably because we only get that when adding the 'address' scope. | |
| 2695 | - // if we didn't want the address, we now need to ask for user details using the login get_userinfo api. IOK 2025-08-12 | |
| 2696 | - // This is also the only way to get "email_verified", so we may want to add a setting that always calls this if neccessary. IOK 2025-08-13 | |
| 2697 | - // Also we don't get this when the state is different from AUTHORIZED. Especially not ABORTED. | |
| 2698 | - // IOK 2025-09-29: This is *no longer the case* . We actually now get userDetails every time we add the relevant scopes, | |
| 2699 | - // so this is now probably dead code. | |
| 2700 | - if ($ready && $express && !$checkout_session && !isset($result['userDetails'])) { | |
| 2701 | - | |
| 2702 | - $sub = isset($result['profile']) && isset($result['profile']['sub']) ? $result['profile']['sub'] : null; | |
| 2703 | - $userinfo = []; | |
| 2704 | - if (!$sub) { | |
| 2705 | - // This should never happen, but be prepared | |
| 2706 | - $message = sprintf(__("Could not get user info for order %1\$d using the userinfo API: %2\$s. Please use the 'get complete transaction details' on the button to try to recover this. ", 'woo-vipps'), $order->get_id(), "No 'sub' passed for user ID" ); | |
| 2707 | - $order->add_order_note($message); | |
| 2708 | - $this->log($message , "error"); | |
| 2709 | - } else { | |
| 2710 | - // If this happens, the merchant *may* be able to retrieve the information from Vipps so add a note for it. | |
| 2711 | - try { | |
| 2712 | - $userinfo = $this->api->get_userinfo($sub); | |
| 2713 | - } catch (Exception $e) { | |
| 2714 | - $message = sprintf(__("Could not get user info for order %1\$d using the userinfo API: %2\$s. Please use the 'get complete transaction details' on the button to try to recover this. ", 'woo-vipps'), $order->get_id(), $e->getMessage()); | |
| 2715 | - $order->add_order_note($message); | |
| 2716 | - $this->log($message, 'woo-vipps', "error"); | |
| 2717 | - } | |
| 2718 | - } | |
| 2719 | - if ($userinfo) { | |
| 2720 | - $userDetails = array( | |
| 2721 | - 'email_verified' => $userinfo['email_verified'], | |
| 2722 | - 'email' => $userinfo['email'], | |
| 2723 | - 'firstName' => $userinfo['given_name'] ?? '', | |
| 2724 | - 'lastName' => $userinfo['family_name'] ?? '', | |
| 2725 | - 'mobileNumber' => $userinfo['phone_number'] ?? '', | |
| 2726 | - 'phoneNumber' => $userinfo['phone_number'] ?? '', | |
| 2727 | - 'userId' => $userinfo['phone_number'] ?? '', | |
| 2728 | - 'sub' => $userinfo['sub'] | |
| 2729 | - ); | |
| 2730 | - | |
| 2731 | - $result['userDetails'] = $userDetails; | |
| 2732 | - | |
| 2733 | - // We may have asked for the address of the customer, so add that too, or a dummy. | |
| 2734 | - if (!isset($result['shippingDetails'])) { | |
| 2735 | - $countries=new WC_Countries(); | |
| 2736 | - $address =[]; | |
| 2737 | - $address['addressLine1'] = ""; | |
| 2738 | - $address['addressLine2'] = ""; | |
| 2739 | - $address['city'] =""; | |
| 2740 | - $address['postCode'] = ""; | |
| 2741 | - $address['country'] = $countries->get_base_country(); | |
| 2742 | - | |
| 2743 | - // This uses other keys than both epayment and checkout, but we'll normalize it later. IOK 2025-08-13 | |
| 2744 | - if (isset($userinfo['address'])) { | |
| 2745 | - $address['addressLine1'] = $userinfo['address']['street_address']; | |
| 2746 | - $address['city'] = $userinfo['address']['region']; | |
| 2747 | - $address['country'] = $userinfo['address']['country']; | |
| 2748 | - $address['postCode'] = $userinfo['address']['postal_code']; | |
| 2749 | - } | |
| 2750 | - $result['shippingDetails'] = ['address' => $address]; | |
| 2751 | - } | |
| 2752 | - } | |
| 2753 | - } | |
| 2754 | - | |
| 2755 | 2668 | if ($ready && ($express || $checkout_session)) { |
| 2756 | - // For Vipps Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10 | |
| 2757 | - // This will also normalize userDetails, adding 'sub' where required and fields for backwards compatibility. 2025-08-12 | |
| 2669 | + // For Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10 | |
| 2670 | + // This will also normalize userDetails, adding 'sub' where possible and fields for backwards compatibility. 2025-08-12 | |
| 2758 | 2671 | $result = $this->ensure_userDetails($result, $order); |
| 2759 | 2672 | |
| 2760 | 2673 | // 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 |
| 2761 | 2674 | // Epayment Express Checkout is of course also significantly different from both the old Express and from Checkout in the formatting here. IOK 2025-08-12 |
| @@ -2827,9 +2740,9 @@ | ||
| 2827 | 2740 | return $result; |
| 2828 | 2741 | } |
| 2829 | 2742 | |
| 2830 | 2743 | |
| 2831 | - // IOK 2024-01-09 If using Vipps Checkout with the BankTransfer method, which is eg. used in Finland, | |
| 2744 | + // IOK 2024-01-09 If using Checkout with the BankTransfer method, which is eg. used in Finland, | |
| 2832 | 2745 | // we are (currently) not receiving any 'state' or 'aggregate', so add this iff the payment is successful. |
| 2833 | 2746 | // The reason for this is that this payment type does not actually use the epayment API at all (!) |
| 2834 | 2747 | // Also moved some other compatibility code here - |
| 2835 | 2748 | // --- reference used to be orderId |
| @@ -2898,9 +2811,9 @@ | ||
| 2898 | 2811 | |
| 2899 | 2812 | return $result; |
| 2900 | 2813 | } |
| 2901 | 2814 | |
| 2902 | - // Vipps Checkout v3 does *not* provide userDetails. Vipps Checkout v2 and epayment *does*. But Checkout additionally allows | |
| 2815 | + // Checkout v3 does *not* provide userDetails. Checkout v2 and epayment *does*. But Checkout additionally allows | |
| 2903 | 2816 | // 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. |
| 2904 | 2817 | // 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 |
| 2905 | 2818 | // if not, we use shippingDetails. IOK 2023-01-10 |
| 2906 | 2819 | // Also, epayment uses mobileNumber and checkout uses phoneNumber, so normalize. |
| @@ -2910,8 +2823,9 @@ | ||
| 2910 | 2823 | // If we have userDetails, use it (ecom API with user data requested - Express Checkout |
| 2911 | 2824 | if (isset($vippsdata['userDetails'])) { |
| 2912 | 2825 | $userDetails = $vippsdata['userDetails']; |
| 2913 | 2826 | // This is the verified user information from the app - this is always the customer for Express Checkout, but not for Checkout IOK 2025-08-12 |
| 2827 | + // Also, it may not always be available - it depends on consent and whether scope was added (probably) in epayment_initate_payment. IOK 2026-08-18 | |
| 2914 | 2828 | $sub = ""; |
| 2915 | 2829 | if (isset($vippsdata['profile']) && isset($vippsdata['profile']['sub'])) { |
| 2916 | 2830 | $sub = $vippsdata['profile']['sub']; |
| 2917 | 2831 | } |
| @@ -3276,9 +3190,13 @@ | ||
| 3276 | 3190 | $is_base64 = $shipping_table ? ( $shipping_table['_is_base64'] ?? false) : false; |
| 3277 | 3191 | |
| 3278 | 3192 | if (is_array($shipping_table) && isset($shipping_table[$key])) { |
| 3279 | 3193 | $decoded = $is_base64 ? @base64_decode($shipping_table[$key]) : $shipping_table[$key]; |
| 3280 | - $shipping_rate = $decoded ? @unserialize($decoded) : null; | |
| 3194 | + | |
| 3195 | + // Ensure no shop manager has injected an evil object (that they would have had to add as a plugin) here. IOK 2026-09-18 | |
| 3196 | + $shipping_rate = $decoded ? @unserialize($decoded, ['allowed_classes' => [WC_Shipping_Rate::class]]) : null; | |
| 3197 | + $shipping_rate = is_a($shipping_rate,'WC_Shipping_Rate') ? $shipping_rate : null; | |
| 3198 | + | |
| 3281 | 3199 | if (!$shipping_rate) { |
| 3282 | 3200 | $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'); |
| 3283 | 3201 | $this->log(sprintf(__("Serialized data was %1\$s", 'woo-vipps'), $decoded), 'error'); |
| 3284 | 3202 | } else { |
| @@ -3298,9 +3216,9 @@ | ||
| 3298 | 3216 | } |
| 3299 | 3217 | } |
| 3300 | 3218 | } |
| 3301 | 3219 | |
| 3302 | - // Possible extra metadata from Vipps Checkout IOK 2023-01-17 | |
| 3220 | + // Possible extra metadata from Checkout IOK 2023-01-17 | |
| 3303 | 3221 | // Store in the order, but also in the shipping rate so it will be visible in the order screen |
| 3304 | 3222 | // along with the shipping ragte |
| 3305 | 3223 | if (isset($shipping['pickupPoint'])) { |
| 3306 | 3224 | $order->update_meta_data('vipps_checkout_pickupPoint', $shipping['pickupPoint']); |
| @@ -3360,9 +3278,9 @@ | ||
| 3360 | 3278 | $methodclass = $methods_classes[$shipping_rate->get_method_id()] ?? null; |
| 3361 | 3279 | $shipping_method = $methodclass ? new $methodclass($shipping_rate->get_instance_id()) : null; |
| 3362 | 3280 | $is_vipps_checkout_shipping = $shipping_method && is_a($shipping_method, 'VippsCheckout_Shipping_Method'); |
| 3363 | 3281 | |
| 3364 | - // Some Vipps Checkout-specific shipping methods calculate the cost in the Vipps window. | |
| 3282 | + // Some Checkout-specific shipping methods calculate the cost in the Vipps window. | |
| 3365 | 3283 | if ($is_vipps_checkout_shipping && $shipping_method->dynamic_cost) { |
| 3366 | 3284 | $vippsamount = intval($order->get_meta('_vipps_amount')); |
| 3367 | 3285 | $shipping_tax_rate = floatval($order->get_meta('_vipps_shipping_tax_rates')); |
| 3368 | 3286 | $compareamount = $ordertotal * 100; |
| @@ -3398,9 +3316,9 @@ | ||
| 3398 | 3316 | |
| 3399 | 3317 | $order->set_total($ordertotal + $total_shipping + $total_shipping_tax); |
| 3400 | 3318 | $order->update_taxes(); // Necessary for the admin view only; does not recalculate order. |
| 3401 | 3319 | |
| 3402 | - // Add an early hook for Vipps Checkout orders with special shipping methods | |
| 3320 | + // Add an early hook for Checkout orders with special shipping methods | |
| 3403 | 3321 | $metadata = $shipping_rate->get_meta_data(); |
| 3404 | 3322 | if (isset($metadata['type'])) { |
| 3405 | 3323 | do_action('woo_vipps_checkout_special_shipping_method', $order, $shipping_rate, $metadata['type']); |
| 3406 | 3324 | } |
| @@ -3416,9 +3334,9 @@ | ||
| 3416 | 3334 | |
| 3417 | 3335 | |
| 3418 | 3336 | // 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. |
| 3419 | 3337 | // If possible and safe, user will be logged in before being sent to the thankyou screen. IOK 2020-10-09 |
| 3420 | - // Same thing for Vipps Checkout, mutatis mutandis. The function below returns false if no customer exists or gets created. | |
| 3338 | + // Same thing for Checkout, mutatis mutandis. The function below returns false if no customer exists or gets created. | |
| 3421 | 3339 | $customer = false; |
| 3422 | 3340 | if ($assigncustomer) { |
| 3423 | 3341 | $customer = Vipps::instance()->express_checkout_get_vipps_customer($order); |
| 3424 | 3342 | } |
| @@ -3426,8 +3344,9 @@ | ||
| 3426 | 3344 | // This would have been used to ensure that we 'enroll' the users the same way as in the Login plugin. Unfortunately, the userId from express checkout isn't |
| 3427 | 3345 | // the same as the 'sub' we get in Login so that must be a future feature. IOK 2020-10-09 |
| 3428 | 3346 | // IOK 2025-08-13 we do get the 'sub' now, at least for express checkout. For Checkout, we would have to compare the email of the user with the verified email |
| 3429 | 3347 | // after calling get_userinfo, so we'll leave that be. |
| 3348 | + // We *maybe* get the sub - it depends on consent, and *maybe* that a scope has been added in epayment_initate_payment. IOK 2026-08-18 | |
| 3430 | 3349 | if (class_exists('VippsWooLogin') && $customer && !is_wp_error($customer) && !get_user_meta($customer->get_id(), '_vipps_phone',true)) { |
| 3431 | 3350 | update_user_meta($customer->get_id(), '_vipps_phone', $billing['phoneNumber']); |
| 3432 | 3351 | if (isset($user['sub'])) { |
| 3433 | 3352 | $userid = $customer->get_id(); |
| @@ -3652,13 +3571,16 @@ | ||
| 3652 | 3571 | $transaction['status'] = ($data['state'] ?? $details['state']); |
| 3653 | 3572 | $transaction['paymentmethod'] = $details['paymentMethod'] ?? ""; |
| 3654 | 3573 | $this->order_set_transaction_metadata($order, $transaction); |
| 3655 | 3574 | |
| 3575 | + // Dont do anything if order is not finalized. LP 2026-08-31 | |
| 3576 | + if (!in_array($newstatus, ['authorized', 'complete', 'cancelled'])) { | |
| 3577 | + return; | |
| 3578 | + } | |
| 3579 | + | |
| 3656 | 3580 | // This order is ready to set order shipping details etc for IOK 2025-09-19 |
| 3657 | - $ready = false; | |
| 3658 | - if (in_array($newstatus, ['authorized', 'complete'])) { | |
| 3659 | - $ready = true; | |
| 3660 | - } | |
| 3581 | + $ready = in_array($newstatus, ['authorized', 'complete']); | |
| 3582 | + | |
| 3661 | 3583 | if ($ready) { |
| 3662 | 3584 | // Failsafe for rare bug when using Klarna Checkout with Vipps as an external payment method |
| 3663 | 3585 | // IOK 2024-01-09 ensure this is called only when order is complete/authorized |
| 3664 | 3586 | $this->reset_erroneous_payment_method($order); |
| @@ -3747,10 +3669,10 @@ | ||
| 3747 | 3669 | // made to the session here will be saved. Sorry. |
| 3748 | 3670 | // 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 |
| 3749 | 3671 | Vipps::instance()->callback_restore_session($order_id); |
| 3750 | 3672 | |
| 3751 | - // For Vipps Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10 | |
| 3752 | - // This will also normalize userDetails, adding 'sub' where required and fields for backwards compatibility. 2025-08-12 | |
| 3673 | + // For Checkout version 3 there are no more userDetails, so we will add it, including defaults for anonymous purchases IOK 2023-01-10 | |
| 3674 | + // This will also normalize userDetails, adding 'sub' where possible and fields for backwards compatibility. 2025-08-12 | |
| 3753 | 3675 | $data = $this->ensure_userDetails($data, $order); |
| 3754 | 3676 | |
| 3755 | 3677 | // Some Express Checkout orders aren't really express checkout orders, but normal orders to which we have |
| 3756 | 3678 | // added scope name, email, phoneNumber. The reason is that we don't care about the address. But then |
| @@ -3840,9 +3762,9 @@ | ||
| 3840 | 3762 | } |
| 3841 | 3763 | do_action('woo_vipps_payment_complete_at_shutdown', $order, $this); |
| 3842 | 3764 | } catch (Exception $e) { |
| 3843 | 3765 | // This is/should be non-critical so just log it. |
| 3844 | - $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"); | |
| 3766 | + $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"); | |
| 3845 | 3767 | } |
| 3846 | 3768 | } |
| 3847 | 3769 | |
| 3848 | 3770 | // This is run on payment complete. Per default will it only add a link to the order confirmation page, but |
| @@ -3949,9 +3871,9 @@ | ||
| 3949 | 3871 | |
| 3950 | 3872 | $contents = WC()->cart->get_cart_contents(); |
| 3951 | 3873 | $contents = apply_filters('woo_vipps_create_express_checkout_cart_contents',$contents); |
| 3952 | 3874 | try { |
| 3953 | - $cart_hash = md5(json_encode(wc_clean($contents)) . WC()->cart->total); | |
| 3875 | + $cart_hash = WC()->cart->get_cart_hash(); | |
| 3954 | 3876 | $order = new WC_Order(); |
| 3955 | 3877 | $order->set_status('pending'); |
| 3956 | 3878 | $order->set_payment_method($this); |
| 3957 | 3879 | if ($ischeckout) { |
| @@ -3962,8 +3884,9 @@ | ||
| 3962 | 3884 | } |
| 3963 | 3885 | // We use 'checkout' as the created_via key as per requests, but allow merchants to use their own. IOK 2022-09-15 |
| 3964 | 3886 | $created_via = apply_filters('woo_vipps_express_checkout_created_via', 'checkout', $order, $ischeckout); |
| 3965 | 3887 | $order->set_created_via($created_via); |
| 3888 | + $order->set_cart_hash($cart_hash); | |
| 3966 | 3889 | |
| 3967 | 3890 | $dummy = sprintf(__('Vipps Express Checkout', 'woo-vipps')); // this is so gettext will find this string. |
| 3968 | 3891 | $dummy = sprintf(__('Vipps Checkout', 'woo-vipps')); // this is so gettext will find this string. |
| 3969 | 3892 | |
| @@ -4125,9 +4048,9 @@ | ||
| 4125 | 4048 | </p> |
| 4126 | 4049 | </div> |
| 4127 | 4050 | <?php endif; ?> |
| 4128 | 4051 | |
| 4129 | - <?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 | |
| 4052 | + <?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 | |
| 4130 | 4053 | $vipps_checkout_activated = get_option('woo_vipps_checkout_activated', false); |
| 4131 | 4054 | ?> |
| 4132 | 4055 | |
| 4133 | 4056 | <?php /* We will *not* allow vipps checkout to be activated at this point, since the product is no longer sold. IOK 2026-04-30 */ ?> |
| @@ -4188,8 +4111,23 @@ | ||
| 4188 | 4111 | // If enabling this, ensure the page in question exists |
| 4189 | 4112 | if ($this->get_option('vipps_checkout_enabled') == 'yes') { |
| 4190 | 4113 | update_option('woo_vipps_checkout_activated', true, true); // This must be true here, but still, make sure |
| 4191 | 4114 | Vipps::instance()->maybe_create_vipps_pages(); |
| 4115 | + } | |
| 4116 | + | |
| 4117 | + // Ensure special page has the necessary shortcode. LP 2026-09-01 | |
| 4118 | + $special_page = get_post(Vipps::get_special_page_id()); | |
| 4119 | + if ($special_page && !has_shortcode($special_page->post_content, 'vipps_special_page')) { | |
| 4120 | + $new_content = $special_page->post_content . "\n\n<!-- wp:shortcode -->[vipps_special_page]<!-- /wp:shortcode -->"; | |
| 4121 | + wp_update_post([ | |
| 4122 | + 'ID' => Vipps::get_special_page_id(), | |
| 4123 | + 'post_content' => $new_content, | |
| 4124 | + ]); | |
| 4125 | + } else if (!Vipps::get_special_page_id()) { | |
| 4126 | + // We shouldn't really get here, the page should be ensured to exist in init. LP 2026-09-03 | |
| 4127 | + /* translators: %s is current method name */ | |
| 4128 | + $this->log(sprintf(__('Missing special page in %s, attempting to fix', 'woo-vipps'), 'process_admin_options'), 'warning'); | |
| 4129 | + Vipps::instance()->ensure_special_page_exists(); | |
| 4192 | 4130 | } |
| 4193 | 4131 | |
| 4194 | 4132 | return $saved; |
| 4195 | 4133 | } |