| 1 |
<?php |
| 2 |
|
| 3 |
defined( 'ABSPATH' ) || exit; |
| 4 |
|
| 5 |
class WC_Vipps_Recurring { |
| 6 |
/** |
| 7 |
* The reference the *Singleton* instance of this class |
| 8 |
*/ |
| 9 |
private static ?WC_Vipps_Recurring $instance = null; |
| 10 |
|
| 11 |
public WC_Vipps_Recurring_Admin_Notices $notices; |
| 12 |
|
| 13 |
public array $ajax_config = []; |
| 14 |
|
| 15 |
/** |
| 16 |
* Returns the *Singleton* instance of this class. |
| 17 |
* |
| 18 |
* @return WC_Vipps_Recurring |
| 19 |
*/ |
| 20 |
public static function get_instance(): WC_Vipps_Recurring { |
| 21 |
if ( null === self::$instance ) { |
| 22 |
self::$instance = new self(); |
| 23 |
} |
| 24 |
|
| 25 |
return self::$instance; |
| 26 |
} |
| 27 |
|
| 28 |
public static function register_hooks() { |
| 29 |
$instance = WC_Vipps_Recurring::get_instance(); |
| 30 |
|
| 31 |
// No longer handled directly here |
| 32 |
// register_activation_hook( WC_VIPPS_MAIN_FILE, [ $instance, 'activate' ] ); |
| 33 |
// register_deactivation_hook( WC_VIPPS_MAIN_FILE, [ $instance, 'deactivate' ] ); |
| 34 |
|
| 35 |
if ( is_admin() ) { |
| 36 |
add_action( 'admin_init', [ $instance, 'admin_init' ] ); |
| 37 |
add_action( 'admin_menu', [ $instance, 'admin_menu' ] ); |
| 38 |
add_action( 'wp_ajax_vipps_recurring_force_check_charge_statuses', [ |
| 39 |
$instance, |
| 40 |
'wp_ajax_vipps_recurring_force_check_charge_statuses' |
| 41 |
] ); |
| 42 |
} |
| 43 |
|
| 44 |
add_action( 'plugins_loaded', [ $instance, 'plugins_loaded' ] ); |
| 45 |
// Declare compatibility with the WooCommerce checkout block |
| 46 |
add_action( 'woocommerce_blocks_loaded', [ $instance, 'woocommerce_blocks_loaded' ] ); |
| 47 |
add_action( 'init', [ $instance, 'init' ] ); |
| 48 |
} |
| 49 |
|
| 50 |
public function activate() { |
| 51 |
global $wp_rewrite; |
| 52 |
|
| 53 |
$this->install(); |
| 54 |
|
| 55 |
$wp_rewrite->flush_rules(); |
| 56 |
add_option( 'woo-vipps-recurring-version', WC_VIPPS_RECURRING_VERSION ); |
| 57 |
} |
| 58 |
|
| 59 |
/** |
| 60 |
* Private clone method to prevent cloning of the instance of the |
| 61 |
* *Singleton* instance. |
| 62 |
* |
| 63 |
* @return void |
| 64 |
*/ |
| 65 |
private function __clone() { |
| 66 |
} |
| 67 |
|
| 68 |
/** |
| 69 |
* Private un-serialize method to prevent un-serializing of the *Singleton* |
| 70 |
* instance. |
| 71 |
* |
| 72 |
* @return void |
| 73 |
*/ |
| 74 |
public function __wakeup() { |
| 75 |
} |
| 76 |
|
| 77 |
/** |
| 78 |
* Protected constructor to prevent creating a new instance of the |
| 79 |
* *Singleton* via the `new` operator from outside of this class. |
| 80 |
*/ |
| 81 |
private function __construct() { |
| 82 |
} |
| 83 |
|
| 84 |
/** |
| 85 |
* @throws WC_Vipps_Recurring_Exception |
| 86 |
* @throws WC_Vipps_Recurring_Temporary_Exception |
| 87 |
* @throws WC_Vipps_Recurring_Config_Exception |
| 88 |
*/ |
| 89 |
public static function deactivate() { |
| 90 |
WC_Vipps_Recurring::get_instance()->gateway()->webhook_teardown(); |
| 91 |
} |
| 92 |
|
| 93 |
public function plugins_loaded() { |
| 94 |
add_filter( 'woocommerce_payment_gateways', [ $this, 'add_gateways' ] ); |
| 95 |
|
| 96 |
// Add custom product settings for Vipps Recurring. |
| 97 |
add_filter( 'woocommerce_product_data_tabs', [ $this, 'woocommerce_product_data_tabs' ] ); |
| 98 |
add_filter( 'woocommerce_product_data_panels', [ $this, 'woocommerce_product_data_panels' ] ); |
| 99 |
add_filter( 'woocommerce_process_product_meta', [ $this, 'woocommerce_process_product_meta' ] ); |
| 100 |
|
| 101 |
// Disable this gateway unless we're purchasing at least one subscription product. |
| 102 |
add_filter( 'woocommerce_available_payment_gateways', [ $this, 'maybe_disable_gateway' ] ); |
| 103 |
|
| 104 |
add_action( 'woocommerce_api_wc_gateway_vipps_recurring', [ $this, 'handle_webhook_callback' ] ); |
| 105 |
|
| 106 |
// Create a real page for Vipps Checkout |
| 107 |
add_filter( 'woocommerce_create_pages', [ $this, 'woocommerce_create_pages' ], 50 ); |
| 108 |
|
| 109 |
} |
| 110 |
|
| 111 |
// Runs possibly before plugins_loaded |
| 112 |
public function woocommerce_blocks_loaded() { |
| 113 |
if ( class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) ) { |
| 114 |
require_once 'wc-vipps-recurring-blocks-support.php'; |
| 115 |
add_action( |
| 116 |
'woocommerce_blocks_payment_method_type_registration', |
| 117 |
function ( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_method_registry ) { |
| 118 |
$payment_method_registry->register( new WC_Vipps_Recurring_Blocks_Support() ); |
| 119 |
} |
| 120 |
); |
| 121 |
} |
| 122 |
} |
| 123 |
|
| 124 |
/** |
| 125 |
* Init the plugin after plugins_loaded so environment variables are set. |
| 126 |
* |
| 127 |
* @since 1.0.0 |
| 128 |
* @version 4.0.0 |
| 129 |
*/ |
| 130 |
public function init() { |
| 131 |
require_once __DIR__ . '/wc-vipps-recurring-rest-api.php'; |
| 132 |
WC_Vipps_Recurring_Rest_Api::get_instance(); |
| 133 |
|
| 134 |
$this->notices = WC_Vipps_Recurring_Admin_Notices::get_instance( WC_VIPPS_RECURRING_MAIN_FILE ); |
| 135 |
|
| 136 |
add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ] ); |
| 137 |
|
| 138 |
add_filter( 'plugin_action_links_' . plugin_basename( WC_VIPPS_MAIN_FILE ), [ |
| 139 |
$this, |
| 140 |
'plugin_action_links' |
| 141 |
] ); |
| 142 |
|
| 143 |
// Add custom cron schedules for Vipps/MobilePay charge polling |
| 144 |
add_filter( 'cron_schedules', [ |
| 145 |
$this, |
| 146 |
'woocommerce_vipps_recurring_add_cron_schedules' |
| 147 |
] ); |
| 148 |
|
| 149 |
// schedule recurring payment charge status checking event |
| 150 |
if ( ! wp_next_scheduled( 'woocommerce_vipps_recurring_check_order_statuses' ) ) { |
| 151 |
wp_schedule_event( time(), 'one_minute', 'woocommerce_vipps_recurring_check_order_statuses' ); |
| 152 |
} |
| 153 |
|
| 154 |
add_action( 'woocommerce_vipps_recurring_check_order_statuses', [ |
| 155 |
$this, |
| 156 |
'check_order_statuses' |
| 157 |
] ); |
| 158 |
|
| 159 |
add_action( 'woocommerce_vipps_recurring_cancel_subscription', [ |
| 160 |
$this->gateway(), |
| 161 |
'cancel_subscription' |
| 162 |
], 10, 2 ); |
| 163 |
|
| 164 |
// Schedule checking if gateway change went through |
| 165 |
if ( ! wp_next_scheduled( 'woocommerce_vipps_recurring_check_gateway_change_request' ) ) { |
| 166 |
wp_schedule_event( time(), 'one_minute', 'woocommerce_vipps_recurring_check_gateway_change_request' ); |
| 167 |
} |
| 168 |
|
| 169 |
add_action( 'woocommerce_vipps_recurring_check_gateway_change_request', [ |
| 170 |
$this, |
| 171 |
'check_gateway_change_agreement_statuses' |
| 172 |
] ); |
| 173 |
|
| 174 |
// Schedule checking for updating payment details |
| 175 |
if ( ! wp_next_scheduled( 'woocommerce_vipps_recurring_update_subscription_details_in_app' ) ) { |
| 176 |
wp_schedule_event( time(), 'one_minute', 'woocommerce_vipps_recurring_update_subscription_details_in_app' ); |
| 177 |
} |
| 178 |
|
| 179 |
add_action( 'woocommerce_vipps_recurring_update_subscription_details_in_app', [ |
| 180 |
$this, |
| 181 |
'update_subscription_details_in_app' |
| 182 |
] ); |
| 183 |
|
| 184 |
// Schedule cleaning up subscriptions that are marked for deletion |
| 185 |
if ( ! wp_next_scheduled( 'woocommerce_vipps_recurring_check_subscriptions_marked_for_deletion' ) ) { |
| 186 |
wp_schedule_event( time(), 'hourly', 'woocommerce_vipps_recurring_check_subscriptions_marked_for_deletion' ); |
| 187 |
} |
| 188 |
|
| 189 |
add_action( 'woocommerce_vipps_recurring_check_subscriptions_marked_for_deletion', [ |
| 190 |
$this, |
| 191 |
'check_subscriptions_marked_for_deletion' |
| 192 |
] ); |
| 193 |
|
| 194 |
add_action( 'woocommerce_vipps_recurring_delete_pending_subscription', [ |
| 195 |
$this->gateway(), |
| 196 |
'maybe_delete_subscription' |
| 197 |
], 10, 1 ); |
| 198 |
|
| 199 |
// Add our own ajax actions |
| 200 |
add_action( 'wp_ajax_wc_vipps_recurring_order_action', [ |
| 201 |
$this, |
| 202 |
'order_handle_vipps_recurring_action' |
| 203 |
] ); |
| 204 |
|
| 205 |
// Custom actions |
| 206 |
add_filter( 'generate_rewrite_rules', [ $this, 'custom_action_endpoints' ] ); |
| 207 |
add_filter( 'query_vars', [ $this, 'custom_action_query_vars' ] ); |
| 208 |
add_filter( 'template_include', [ $this, 'custom_action_page_template' ] ); |
| 209 |
} |
| 210 |
|
| 211 |
/** |
| 212 |
* Admin only dashboard |
| 213 |
*/ |
| 214 |
public function admin_init() { |
| 215 |
add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); |
| 216 |
|
| 217 |
add_action( 'admin_head', [ $this, 'admin_head' ] ); |
| 218 |
|
| 219 |
// Add capture button if order is not captured |
| 220 |
add_action( 'woocommerce_order_item_add_action_buttons', [ |
| 221 |
$this, |
| 222 |
'order_item_add_action_buttons' |
| 223 |
] ); |
| 224 |
|
| 225 |
if ( $this->gateway()->test_mode ) { |
| 226 |
$notice = __( 'Vipps/MobilePay Recurring Payments is currently in test mode - no real transactions will occur. Disable test mode when you are ready to go live!', 'woo-vipps' ); |
| 227 |
$this->notices->warning( $notice ); |
| 228 |
} |
| 229 |
|
| 230 |
// Load correct list table classes for current screen. |
| 231 |
add_action( 'current_screen', [ $this, 'setup_screen' ] ); |
| 232 |
|
| 233 |
if ( isset( $_REQUEST['statuses_checked'] ) ) { |
| 234 |
$this->notices->success( __( 'Successfully checked the status of these charges', 'woo-vipps' ) ); |
| 235 |
} |
| 236 |
|
| 237 |
// Initialize webhooks if we haven't already |
| 238 |
if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) { |
| 239 |
if ( WC_Vipps_Recurring_Helper::is_connected() ) { |
| 240 |
if ( empty( get_option( WC_Vipps_Recurring_Helper::OPTION_WEBHOOKS ) ) ) { |
| 241 |
$this->gateway()->webhook_initialize(); |
| 242 |
} else { |
| 243 |
$ok = $this->gateway()->webhook_ensure_this_site(); |
| 244 |
|
| 245 |
if ( ! $ok ) { |
| 246 |
$this->gateway()->webhook_initialize(); |
| 247 |
} |
| 248 |
} |
| 249 |
} |
| 250 |
} |
| 251 |
|
| 252 |
// $test = $this->gateway()->check_charge_status(931); |
| 253 |
|
| 254 |
// Show Vipps Login notice for a maximum of 10 days |
| 255 |
// 1636066799 = 04-11-2021 23:59:59 UTC |
| 256 |
// if ( ! class_exists( 'VippsWooLogin' ) && time() < 1636066799 ) { |
| 257 |
// $vipps_login_plugin_url = 'https://wordpress.org/plugins/login-with-vipps'; |
| 258 |
// if ( get_locale() === 'nb_NO' ) { |
| 259 |
// $vipps_login_plugin_url = 'https://nb.wordpress.org/plugins/login-with-vipps'; |
| 260 |
// } |
| 261 |
// |
| 262 |
// $this->notices->campaign( |
| 263 |
// /* translators: %1$s URL to login-with-vipps, %2$s translation for "here" */ |
| 264 |
// sprintf( __( 'Login with Vipps is available for WooCommerce. Super-easy and safer login for your customers - no more usernames and passwords. Get started <a href="%1$s" target="_blank">%2$s</a>!', 'woo-vipps' ), $vipps_login_plugin_url, __( 'here', 'woo-vipps' ) ), |
| 265 |
// 'login_promotion', |
| 266 |
// true, |
| 267 |
// 'assets/images/vipps-logg-inn-neg.png', |
| 268 |
// 'login-promotion' |
| 269 |
// ); |
| 270 |
// } |
| 271 |
} |
| 272 |
|
| 273 |
public function gateway(): WC_Gateway_Vipps_Recurring { |
| 274 |
require_once( "wc-gateway-vipps-recurring.php" ); |
| 275 |
|
| 276 |
return WC_Gateway_Vipps_Recurring::get_instance(); |
| 277 |
} |
| 278 |
|
| 279 |
/** |
| 280 |
* Upgrade routines |
| 281 |
*/ |
| 282 |
public function upgrade() { |
| 283 |
global $wpdb, $wp_rewrite; |
| 284 |
|
| 285 |
$version = get_option( 'woo-vipps-recurring-version' ); |
| 286 |
|
| 287 |
// Update 1.8.1: add back _vipps_recurring_pending_charge and _charge_id |
| 288 |
if ( version_compare( $version, '1.8.1', '<' ) ) { |
| 289 |
$results = $wpdb->get_results( "SELECT wp_posts.id FROM ( |
| 290 |
SELECT DISTINCT post_id as id FROM wp_postmeta as m |
| 291 |
WHERE EXISTS (SELECT * FROM wp_postmeta WHERE post_id = m.post_id AND meta_key = '_vipps_recurring_failed_charge_reason') |
| 292 |
AND NOT EXISTS (SELECT * FROM wp_postmeta WHERE post_id = m.post_id AND meta_key = '_vipps_recurring_pending_charge') |
| 293 |
) as lookup |
| 294 |
JOIN wp_posts ON (wp_posts.id = lookup.id) |
| 295 |
ORDER BY wp_posts.post_date DESC", ARRAY_A ); |
| 296 |
|
| 297 |
WC_Vipps_Recurring_Logger::log( sprintf( 'Running 1.8.1 update, affecting orders with IDs: %s', implode( ',', array_map( function ( $item ) { |
| 298 |
return $item['id']; |
| 299 |
}, $results ) ) ) ); |
| 300 |
|
| 301 |
foreach ( $results as $row ) { |
| 302 |
$order = wc_get_order( $row['id'] ); |
| 303 |
WC_Vipps_Recurring_Helper::set_order_charge_not_failed( $order, WC_Vipps_Recurring_Helper::get_transaction_id_for_order( $order ) ); |
| 304 |
$order->save(); |
| 305 |
} |
| 306 |
} |
| 307 |
|
| 308 |
// Update 1.8.2: migrate failed statuses to subscription too |
| 309 |
if ( version_compare( $version, '1.8.2', '<' ) ) { |
| 310 |
$results = $wpdb->get_results( "SELECT wp_posts.id |
| 311 |
FROM ( |
| 312 |
SELECT DISTINCT post_id as id |
| 313 |
FROM wp_postmeta as m |
| 314 |
WHERE EXISTS(SELECT * |
| 315 |
FROM wp_postmeta |
| 316 |
WHERE post_id = m.post_id |
| 317 |
AND meta_key = '_vipps_recurring_failed_charge_reason') |
| 318 |
) as lookup |
| 319 |
JOIN wp_posts ON (wp_posts.id = lookup.id) |
| 320 |
ORDER BY wp_posts.post_date DESC", ARRAY_A ); |
| 321 |
|
| 322 |
WC_Vipps_Recurring_Logger::log( sprintf( 'Running 1.8.2 update, affecting orders with IDs: %s', implode( ',', array_map( function ( $item ) { |
| 323 |
return $item['id']; |
| 324 |
}, $results ) ) ) ); |
| 325 |
|
| 326 |
foreach ( $results as $row ) { |
| 327 |
$order = wc_get_order( $row['id'] ); |
| 328 |
|
| 329 |
$subscriptions = WC_Vipps_Recurring_Helper::get_subscriptions_for_order( $order ); |
| 330 |
$subscription = $subscriptions[ array_key_first( $subscriptions ) ]; |
| 331 |
|
| 332 |
if ( ! $subscription ) { |
| 333 |
continue; |
| 334 |
} |
| 335 |
|
| 336 |
$failure_reason = WC_Vipps_Recurring_Helper::get_meta( $order, WC_Vipps_Recurring_Helper::META_CHARGE_FAILED_REASON ); |
| 337 |
if ( $failure_reason ) { |
| 338 |
WC_Vipps_Recurring_Helper::update_meta_data( $subscription, WC_Vipps_Recurring_Helper::META_SUBSCRIPTION_LATEST_FAILED_CHARGE_REASON, $failure_reason ); |
| 339 |
} |
| 340 |
|
| 341 |
$failure_description = WC_Vipps_Recurring_Helper::get_meta( $order, WC_Vipps_Recurring_Helper::META_CHARGE_FAILED_DESCRIPTION ); |
| 342 |
if ( $failure_description ) { |
| 343 |
WC_Vipps_Recurring_Helper::update_meta_data( $subscription, WC_Vipps_Recurring_Helper::META_SUBSCRIPTION_LATEST_FAILED_CHARGE_DESCRIPTION, $failure_description ); |
| 344 |
} |
| 345 |
|
| 346 |
$subscription->save(); |
| 347 |
} |
| 348 |
} |
| 349 |
|
| 350 |
// Flush permalinks when updating to version 1.20.2 |
| 351 |
if ( version_compare( $version, '1.20.2', '<' ) ) { |
| 352 |
$wp_rewrite->flush_rules(); |
| 353 |
} |
| 354 |
|
| 355 |
// Copy MSN, client id, client secret and subscription key to our new test fields if test mode is enabled. |
| 356 |
if ( $this->gateway()->test_mode && version_compare( $version, '2.0.0', '<' ) ) { |
| 357 |
$this->gateway()->update_option( 'test_merchant_serial_number', $this->gateway()->get_option( 'merchant_serial_number' ) ); |
| 358 |
$this->gateway()->update_option( 'test_client_id', $this->gateway()->get_option( 'client_id' ) ); |
| 359 |
$this->gateway()->update_option( 'test_secret_key', $this->gateway()->get_option( 'secret_key' ) ); |
| 360 |
$this->gateway()->update_option( 'test_subscription_key', $this->gateway()->get_option( 'subscription_key' ) ); |
| 361 |
} |
| 362 |
|
| 363 |
if ( $version !== WC_VIPPS_RECURRING_VERSION ) { |
| 364 |
update_option( 'woo-vipps-recurring-version', WC_VIPPS_RECURRING_VERSION ); |
| 365 |
} |
| 366 |
} |
| 367 |
|
| 368 |
/** |
| 369 |
* Inject admin ahead |
| 370 |
*/ |
| 371 |
public function admin_head() { |
| 372 |
$icon = plugins_url( 'assets/images/' . $this->gateway()->brand . '-mark-icon.svg', WC_VIPPS_RECURRING_MAIN_FILE ); |
| 373 |
|
| 374 |
?> |
| 375 |
<style> |
| 376 |
#woocommerce-product-data ul.wc-tabs li.wc_vipps_recurring_options a:before { |
| 377 |
background-image: url( <?php echo $icon ?> ); |
| 378 |
} |
| 379 |
</style> |
| 380 |
<?php |
| 381 |
} |
| 382 |
|
| 383 |
public function gateway_should_be_active( array $methods = [] ) { |
| 384 |
// Disable our gateway if the standard Vipps payment method is present. |
| 385 |
$active = ! isset( $methods['vipps'] ); |
| 386 |
|
| 387 |
return apply_filters( 'wc_vipps_recurring_cart_has_subscription_product', $active, WC()->cart->get_cart_contents() ); |
| 388 |
} |
| 389 |
|
| 390 |
public function maybe_disable_gateway( $methods ) { |
| 391 |
if ( is_admin() || ! is_checkout() ) { |
| 392 |
return $methods; |
| 393 |
} |
| 394 |
|
| 395 |
$show_gateway = $this->gateway_should_be_active( $methods ); |
| 396 |
|
| 397 |
if ( ! $show_gateway ) { |
| 398 |
unset( $methods['vipps_recurring'] ); |
| 399 |
} |
| 400 |
|
| 401 |
return $methods; |
| 402 |
} |
| 403 |
|
| 404 |
/** |
| 405 |
* @return string |
| 406 |
*/ |
| 407 |
public function handle_check_statuses_bulk_action(): string { |
| 408 |
$sendback = remove_query_arg( [ 'orders' ], wp_get_referer() ); |
| 409 |
|
| 410 |
if ( isset( $_GET['orders'] ) ) { |
| 411 |
$order_ids = $_GET['orders']; |
| 412 |
|
| 413 |
foreach ( $order_ids as $order_id ) { |
| 414 |
// check charge status |
| 415 |
$this->gateway()->check_charge_status( $order_id, true ); |
| 416 |
} |
| 417 |
|
| 418 |
$sendback = add_query_arg( 'statuses_checked', 1, $sendback ); |
| 419 |
} |
| 420 |
|
| 421 |
return $sendback; |
| 422 |
} |
| 423 |
|
| 424 |
/** |
| 425 |
* Setup the screen for our special setting and action tables |
| 426 |
*/ |
| 427 |
public function setup_screen() { |
| 428 |
global $wc_vipps_recurring_list_table_pending_charges, |
| 429 |
$wc_vipps_recurring_list_table_failed_charges; |
| 430 |
|
| 431 |
$screen_id = false; |
| 432 |
|
| 433 |
if ( function_exists( 'get_current_screen' ) ) { |
| 434 |
$screen = get_current_screen(); |
| 435 |
$screen_id = isset( $screen, $screen->id ) ? $screen->id : ''; |
| 436 |
} |
| 437 |
|
| 438 |
if ( ! empty( $_REQUEST['screen'] ) ) { |
| 439 |
$screen_id = wc_clean( wp_unslash( $_REQUEST['screen'] ) ); |
| 440 |
} |
| 441 |
|
| 442 |
if ( $screen_id === 'settings_page_woo-vipps-recurring' ) { |
| 443 |
include_once 'admin/list-tables/wc-vipps-recurring-list-table-pending-charges.php'; |
| 444 |
include_once 'admin/list-tables/wc-vipps-recurring-list-table-failed-charges.php'; |
| 445 |
|
| 446 |
$wc_vipps_recurring_list_table_pending_charges = new WC_Vipps_Recurring_Admin_List_Pending_Charges( [ |
| 447 |
'screen' => $screen_id . '_pending-charges' |
| 448 |
] ); |
| 449 |
$wc_vipps_recurring_list_table_failed_charges = new WC_Vipps_Recurring_Admin_List_Failed_Charges( [ |
| 450 |
'screen' => $screen_id . '_failed-charges' |
| 451 |
] ); |
| 452 |
} |
| 453 |
|
| 454 |
if ( $wc_vipps_recurring_list_table_pending_charges |
| 455 |
&& $wc_vipps_recurring_list_table_pending_charges->current_action() |
| 456 |
&& $wc_vipps_recurring_list_table_pending_charges->current_action() === 'check_status' ) { |
| 457 |
$sendback = $this->handle_check_statuses_bulk_action(); |
| 458 |
|
| 459 |
wp_redirect( $sendback ); |
| 460 |
} |
| 461 |
|
| 462 |
if ( $wc_vipps_recurring_list_table_failed_charges |
| 463 |
&& $wc_vipps_recurring_list_table_failed_charges->current_action() |
| 464 |
&& $wc_vipps_recurring_list_table_failed_charges->current_action() === 'check_status' ) { |
| 465 |
$sendback = $this->handle_check_statuses_bulk_action(); |
| 466 |
|
| 467 |
wp_redirect( $sendback ); |
| 468 |
} |
| 469 |
|
| 470 |
// Ensure the table handler is only loaded once. Prevents multiple loads if a plugin calls check_ajax_referer many times. |
| 471 |
remove_action( 'current_screen', [ $this, 'setup_screen' ] ); |
| 472 |
} |
| 473 |
|
| 474 |
/** |
| 475 |
* Make admin menu entry |
| 476 |
*/ |
| 477 |
public function admin_menu() { |
| 478 |
add_options_page( |
| 479 |
__( 'Vipps/MobilePay Recurring Payments', 'woo-vipps' ), |
| 480 |
__( 'Vipps/MobilePay Recurring Payments', 'woo-vipps' ), |
| 481 |
'manage_options', |
| 482 |
'woo-vipps-recurring', |
| 483 |
[ $this, 'admin_menu_page_html' ] |
| 484 |
); |
| 485 |
} |
| 486 |
|
| 487 |
/** |
| 488 |
* Admin menu page HTML |
| 489 |
*/ |
| 490 |
public function admin_menu_page_html() { |
| 491 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 492 |
return; |
| 493 |
} |
| 494 |
|
| 495 |
include __DIR__ . '/pages/admin/vipps-recurring-admin-menu-page.php'; |
| 496 |
} |
| 497 |
|
| 498 |
public function custom_action_endpoints( $rewrites ) { |
| 499 |
$rewrites->rules = array_merge( |
| 500 |
[ 'vipps-mobilepay-recurring-payment/?$' => 'index.php?vipps_recurring_action=payment-redirect' ], |
| 501 |
$rewrites->rules |
| 502 |
); |
| 503 |
|
| 504 |
return $rewrites; |
| 505 |
} |
| 506 |
|
| 507 |
public function custom_action_query_vars( $query_vars ) { |
| 508 |
$query_vars[] = 'vipps_recurring_action'; |
| 509 |
|
| 510 |
return $query_vars; |
| 511 |
} |
| 512 |
|
| 513 |
public function custom_action_page_template( $original_template ) { |
| 514 |
$custom_action = get_query_var( 'vipps_recurring_action' ); |
| 515 |
|
| 516 |
if ( ! empty ( $custom_action ) ) { |
| 517 |
include WC_VIPPS_RECURRING_PLUGIN_PATH . '/includes/pages/payment-redirect-page.php'; |
| 518 |
die; |
| 519 |
} |
| 520 |
|
| 521 |
return $original_template; |
| 522 |
} |
| 523 |
|
| 524 |
public function maybe_create_checkout_page() { |
| 525 |
$checkout_page_id = wc_get_page_id( 'vipps_mobilepay_recurring_checkout' ); |
| 526 |
$should_create_pages = ! $checkout_page_id || ! get_post_status( $checkout_page_id ); |
| 527 |
|
| 528 |
if ( ! $should_create_pages ) { |
| 529 |
return; |
| 530 |
} |
| 531 |
|
| 532 |
// Piggybacks off of WooCommerce's default logic for creating the checkout page. |
| 533 |
// This is possible because we override the shortcode. |
| 534 |
delete_option( 'woocommerce_vipps_recurring_checkout_page_id' ); |
| 535 |
WC_Install::create_pages(); |
| 536 |
} |
| 537 |
|
| 538 |
/** |
| 539 |
* Vipps Checkout replaces the default checkout page, and currently uses its own page for this which needs to exist |
| 540 |
*/ |
| 541 |
public function woocommerce_create_pages( $data ) { |
| 542 |
$checkout_enabled = get_option( WC_Vipps_Recurring_Helper::OPTION_CHECKOUT_ENABLED, false ); |
| 543 |
if ( ! $checkout_enabled ) { |
| 544 |
return $data; |
| 545 |
} |
| 546 |
|
| 547 |
$data['vipps_recurring_checkout'] = array( |
| 548 |
'name' => _x( 'vipps_recurring_checkout', 'Page slug', 'woo-vipps' ), |
| 549 |
'title' => _x( 'Vipps MobilePay Recurring Checkout', 'Page title', 'woo-vipps' ), |
| 550 |
'content' => '<!-- wp:shortcode -->[' . 'vipps_recurring_checkout' . ']<!-- /wp:shortcode -->', |
| 551 |
); |
| 552 |
|
| 553 |
return $data; |
| 554 |
} |
| 555 |
|
| 556 |
/** |
| 557 |
* Force check status of all pending charges |
| 558 |
*/ |
| 559 |
public function wp_ajax_vipps_recurring_force_check_charge_statuses(): void { |
| 560 |
try { |
| 561 |
/* translators: amount of orders checked */ |
| 562 |
echo sprintf( __( 'Done. Checked the status of %s orders', 'woo-vipps' ), count( $this->check_order_statuses( - 1 ) ) ); |
| 563 |
} catch ( Exception $e ) { |
| 564 |
echo __( 'Failed to finish checking the status of all orders. Please try again.', 'woo-vipps' ); |
| 565 |
} |
| 566 |
|
| 567 |
wp_die(); |
| 568 |
} |
| 569 |
|
| 570 |
/** |
| 571 |
* @param $tabs |
| 572 |
* |
| 573 |
* @return mixed |
| 574 |
*/ |
| 575 |
public function woocommerce_product_data_tabs( $tabs ) { |
| 576 |
$tabs['wc_vipps_recurring'] = [ |
| 577 |
'label' => __( 'Vipps/MobilePay Recurring Payments', 'woo-vipps' ), |
| 578 |
'target' => 'wc_vipps_recurring_product_data', |
| 579 |
'priority' => 100, |
| 580 |
]; |
| 581 |
|
| 582 |
return $tabs; |
| 583 |
} |
| 584 |
|
| 585 |
/** |
| 586 |
* Tab content |
| 587 |
*/ |
| 588 |
public function woocommerce_product_data_panels(): void { |
| 589 |
echo '<div id="wc_vipps_recurring_product_data" class="panel woocommerce_options_panel hidden">'; |
| 590 |
|
| 591 |
woocommerce_wp_checkbox( [ |
| 592 |
'id' => WC_Vipps_Recurring_Helper::META_PRODUCT_DIRECT_CAPTURE, |
| 593 |
'value' => get_post_meta( get_the_ID(), WC_Vipps_Recurring_Helper::META_PRODUCT_DIRECT_CAPTURE, true ), |
| 594 |
'label' => __( 'Capture payment instantly', 'woo-vipps' ), |
| 595 |
'description' => __( 'Capture payment instantly even if the product is not virtual. Please make sure you are following the local jurisdiction in your country when using this option.', 'woo-vipps' ), |
| 596 |
'desc_tip' => true, |
| 597 |
] ); |
| 598 |
|
| 599 |
woocommerce_wp_select( [ |
| 600 |
'id' => WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_SOURCE, |
| 601 |
'value' => get_post_meta( get_the_ID(), WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_SOURCE, true ) ?: 'title', |
| 602 |
'label' => __( 'Description source', 'woo-vipps' ), |
| 603 |
'description' => __( 'Where we should source the agreement description from. Displayed in the Vipps/MobilePay app.', 'woo-vipps' ), |
| 604 |
'desc_tip' => true, |
| 605 |
'options' => [ |
| 606 |
'none' => __( 'None', 'woo-vipps' ), |
| 607 |
'short_description' => __( 'Product short description', 'woo-vipps' ), |
| 608 |
'custom' => __( 'Custom', 'woo-vipps' ) |
| 609 |
] |
| 610 |
] ); |
| 611 |
|
| 612 |
woocommerce_wp_text_input( [ |
| 613 |
'id' => WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_TEXT, |
| 614 |
'value' => get_post_meta( get_the_ID(), WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_TEXT, true ), |
| 615 |
'label' => __( 'Custom description', 'woo-vipps' ), |
| 616 |
'description' => __( 'If the description source is set to "custom" this text will be used.', 'woo-vipps' ), |
| 617 |
'placeholder' => __( 'Max 100 characters', 'woo-vipps' ), |
| 618 |
'desc_tip' => true, |
| 619 |
] ); |
| 620 |
|
| 621 |
echo '</div>'; |
| 622 |
} |
| 623 |
|
| 624 |
/** |
| 625 |
* Save our custom fields |
| 626 |
* |
| 627 |
* @param $post_id |
| 628 |
*/ |
| 629 |
public function woocommerce_process_product_meta( $post_id ) { |
| 630 |
$capture_instantly = isset( $_POST[ WC_Vipps_Recurring_Helper::META_PRODUCT_DIRECT_CAPTURE ] ) ? 'yes' : 'no'; |
| 631 |
update_post_meta( $post_id, WC_Vipps_Recurring_Helper::META_PRODUCT_DIRECT_CAPTURE, $capture_instantly ); |
| 632 |
|
| 633 |
update_post_meta( $post_id, WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_SOURCE, $_POST[ WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_SOURCE ] ); |
| 634 |
update_post_meta( $post_id, WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_TEXT, $_POST[ WC_Vipps_Recurring_Helper::META_PRODUCT_DESCRIPTION_TEXT ] ?? '' ); |
| 635 |
} |
| 636 |
|
| 637 |
/** |
| 638 |
* @param $order |
| 639 |
*/ |
| 640 |
public function order_item_add_action_buttons( $order ): void { |
| 641 |
$this->order_item_add_capture_button( $order ); |
| 642 |
} |
| 643 |
|
| 644 |
/** |
| 645 |
* @param $order |
| 646 |
*/ |
| 647 |
public function order_item_add_capture_button( $order ): void { |
| 648 |
if ( $order->get_type() !== 'shop_order' ) { |
| 649 |
return; |
| 650 |
} |
| 651 |
|
| 652 |
$payment_method = WC_Vipps_Recurring_Helper::get_payment_method( $order ); |
| 653 |
if ( $payment_method !== $this->gateway()->id ) { |
| 654 |
// If this is not the payment method, an agreement would not be available. |
| 655 |
return; |
| 656 |
} |
| 657 |
|
| 658 |
$show_capture_button = WC_Vipps_Recurring_Helper::can_capture_charge_for_order( $order ); |
| 659 |
|
| 660 |
if ( ! apply_filters( 'wc_vipps_recurring_show_capture_button', $show_capture_button, $order ) ) { |
| 661 |
return; |
| 662 |
} |
| 663 |
|
| 664 |
$is_captured = WC_Vipps_Recurring_Helper::is_charge_captured_for_order( $order ); |
| 665 |
|
| 666 |
if ( $show_capture_button && ! $is_captured ) { |
| 667 |
$logo = plugins_url( 'assets/images/' . $this->gateway()->brand . '-logo-white.svg', WC_VIPPS_RECURRING_MAIN_FILE ); |
| 668 |
|
| 669 |
print '<button type="button" data-order-id="' . $order->get_id() . '" data-action="capture_payment" class="button generate-items capture-payment-button ' . $this->gateway()->brand . '"><img border="0" style="display:inline;height:2ex;vertical-align:text-bottom" class="inline" alt="0" src="' . $logo . '"/> ' . __( 'Capture payment', 'woo-vipps' ) . '</button>'; |
| 670 |
} |
| 671 |
} |
| 672 |
|
| 673 |
public function order_handle_vipps_recurring_action() { |
| 674 |
check_ajax_referer( 'vipps_recurring_ajax_nonce', 'nonce' ); |
| 675 |
|
| 676 |
$order = wc_get_order( intval( $_REQUEST['orderId'] ) ); |
| 677 |
if ( ! is_a( $order, 'WC_Order' ) ) { |
| 678 |
return; |
| 679 |
} |
| 680 |
|
| 681 |
if ( $order->get_payment_method() != $this->gateway()->id ) { |
| 682 |
return; |
| 683 |
} |
| 684 |
|
| 685 |
$action = isset( $_REQUEST['do'] ) ? sanitize_title( $_REQUEST['do'] ) : 'none'; |
| 686 |
|
| 687 |
if ( $action == 'capture_payment' ) { |
| 688 |
$this->gateway()->maybe_capture_payment( $order->get_id() ); |
| 689 |
} |
| 690 |
|
| 691 |
print "1"; |
| 692 |
} |
| 693 |
|
| 694 |
/** |
| 695 |
* Check charge statuses scheduled action |
| 696 |
* |
| 697 |
* @param int|null $limit |
| 698 |
* |
| 699 |
* @return array |
| 700 |
*/ |
| 701 |
public function check_order_statuses( $limit = '' ): array { |
| 702 |
if ( empty( $limit ) ) { |
| 703 |
$limit = $this->gateway()->check_charges_amount; |
| 704 |
} |
| 705 |
|
| 706 |
$options = [ |
| 707 |
'limit' => $limit, |
| 708 |
'payment_method' => $this->gateway()->id |
| 709 |
]; |
| 710 |
|
| 711 |
$options = WC_Vipps_Recurring_Helper::add_meta_query_to_args( $options, WC_Vipps_Recurring_Helper::META_CHARGE_PENDING, '=', 1, $this->gateway()->use_high_performance_order_storage() ); |
| 712 |
|
| 713 |
if ( $this->gateway()->check_charges_sort_order === 'rand' ) { |
| 714 |
$options['orderby'] = 'rand'; |
| 715 |
} else { |
| 716 |
$options['orderby'] = 'post_date'; |
| 717 |
$options['order'] = $this->gateway()->check_charges_sort_order; |
| 718 |
} |
| 719 |
|
| 720 |
remove_all_filters( 'posts_orderby' ); |
| 721 |
$orders = wc_get_orders( $options ); |
| 722 |
|
| 723 |
foreach ( $orders as $order ) { |
| 724 |
$order_id = WC_Vipps_Recurring_Helper::get_id( $order ); |
| 725 |
|
| 726 |
do_action( 'wc_vipps_recurring_before_cron_check_order_status', $order_id ); |
| 727 |
$this->gateway()->check_charge_status( $order_id ); |
| 728 |
do_action( 'wc_vipps_recurring_after_cron_check_order_status', $order_id ); |
| 729 |
} |
| 730 |
|
| 731 |
return $orders; |
| 732 |
} |
| 733 |
|
| 734 |
/** |
| 735 |
* Check the status of gateway change requests |
| 736 |
*/ |
| 737 |
public function check_gateway_change_agreement_statuses() { |
| 738 |
$args = [ |
| 739 |
'subscription_status' => [ 'active', 'pending', 'on-hold' ], |
| 740 |
]; |
| 741 |
|
| 742 |
$args = WC_Vipps_Recurring_Helper::add_meta_query_to_args( $args, WC_Vipps_Recurring_Helper::META_SUBSCRIPTION_WAITING_FOR_GATEWAY_CHANGE, '=', 1, $this->gateway()->use_high_performance_order_storage() ); |
| 743 |
|
| 744 |
$subscriptions = wcs_get_subscriptions( $args ); |
| 745 |
|
| 746 |
foreach ( $subscriptions as $subscription ) { |
| 747 |
// check charge status |
| 748 |
$this->gateway()->maybe_process_gateway_change( $subscription->get_id() ); |
| 749 |
} |
| 750 |
} |
| 751 |
|
| 752 |
/** |
| 753 |
* Update a subscription's details in the app |
| 754 |
*/ |
| 755 |
public function update_subscription_details_in_app() { |
| 756 |
$args = [ |
| 757 |
'subscriptions_per_page' => 5, |
| 758 |
'subscription_status' => [ 'active', 'pending-cancel', 'cancelled', 'on-hold' ], |
| 759 |
]; |
| 760 |
|
| 761 |
$args = WC_Vipps_Recurring_Helper::add_meta_query_to_args( $args, WC_Vipps_Recurring_Helper::META_SUBSCRIPTION_UPDATE_IN_APP, '=', 1, $this->gateway()->use_high_performance_order_storage() ); |
| 762 |
|
| 763 |
$subscriptions = wcs_get_subscriptions( $args ); |
| 764 |
|
| 765 |
foreach ( $subscriptions as $subscription ) { |
| 766 |
// check charge status |
| 767 |
$this->gateway()->maybe_update_subscription_details_in_app( $subscription->get_id() ); |
| 768 |
} |
| 769 |
} |
| 770 |
|
| 771 |
public function check_subscriptions_marked_for_deletion() { |
| 772 |
$options = [ |
| 773 |
'limit' => 25, |
| 774 |
'payment_method' => $this->gateway()->id, |
| 775 |
]; |
| 776 |
|
| 777 |
$options = WC_Vipps_Recurring_Helper::add_meta_query_to_args($options, WC_Vipps_Recurring_Helper::META_SUBSCRIPTION_MARKED_FOR_DELETION, '=', 1, $this->gateway()->use_high_performance_order_storage()); |
| 778 |
|
| 779 |
$subscriptions = wcs_get_subscriptions( $options ); |
| 780 |
|
| 781 |
WC_Vipps_Recurring_Logger::log( sprintf( "Running check_subscriptions_marked_for_deletion for %s subscriptions", count( $subscriptions ) ) ); |
| 782 |
|
| 783 |
foreach ( $subscriptions as $subscription ) { |
| 784 |
$subscription_id = WC_Vipps_Recurring_Helper::get_id( $subscription ); |
| 785 |
|
| 786 |
// Check the status of this subscription's latest order charge just in case. |
| 787 |
$latest_order = $subscription->get_last_order( 'all' ); |
| 788 |
$latest_order_id = WC_Vipps_Recurring_Helper::get_id( $latest_order ); |
| 789 |
|
| 790 |
do_action( 'wc_vipps_recurring_before_cron_check_order_status', $latest_order_id ); |
| 791 |
$this->gateway()->check_charge_status( $latest_order_id ); |
| 792 |
do_action( 'wc_vipps_recurring_after_cron_check_order_status', $latest_order_id ); |
| 793 |
|
| 794 |
// Hydrate objects |
| 795 |
$latest_order = wc_get_order( $latest_order_id ); |
| 796 |
$subscription = wcs_get_subscription( $subscription_id ); |
| 797 |
|
| 798 |
// If this subscription has been manually updated in the mean-time, we no longer want to delete it. |
| 799 |
// Similarly, if it has a billing email we don't want to delete it. |
| 800 |
$empty_email = trim( $subscription->get_billing_email() ) === trim( WC_Vipps_Recurring_Helper::FAKE_USER_EMAIL ) || ! $subscription->get_billing_email(); |
| 801 |
|
| 802 |
WC_Vipps_Recurring_Logger::log( sprintf( "Checking if subscription %s should be deleted (status: %s, empty email: %s, is renewal: %s).", $subscription_id, $subscription->get_status(), $empty_email ? 'Yes' : 'No', wcs_order_contains_renewal( $latest_order ) ? 'Yes' : 'No' ) ); |
| 803 |
|
| 804 |
$allowed_statuses = [ |
| 805 |
'pending', |
| 806 |
'cancelled', |
| 807 |
'pending-cancel' |
| 808 |
]; |
| 809 |
|
| 810 |
if ( ! in_array( $subscription->get_status( 'edit' ), $allowed_statuses ) |
| 811 |
|| ! $empty_email |
| 812 |
|| wcs_order_contains_renewal( $latest_order ) ) { |
| 813 |
WC_Vipps_Recurring_Logger::log( sprintf( "Removing subscription %s from the deletion queue as it should no longer be deleted.", $subscription_id ) ); |
| 814 |
|
| 815 |
WC_Vipps_Recurring_Helper::delete_meta_data( $subscription, WC_Vipps_Recurring_Helper::META_SUBSCRIPTION_MARKED_FOR_DELETION ); |
| 816 |
$subscription->save(); |
| 817 |
|
| 818 |
continue; |
| 819 |
} |
| 820 |
|
| 821 |
WC_Vipps_Recurring_Logger::log( sprintf( "Deleting subscription %s and latest order %s.", $subscription_id, $latest_order_id ) ); |
| 822 |
$latest_order->delete(); |
| 823 |
$subscription->delete(); |
| 824 |
} |
| 825 |
} |
| 826 |
|
| 827 |
/** |
| 828 |
* Adds plugin action links. |
| 829 |
* |
| 830 |
* @since 1.0.0 |
| 831 |
* @version 4.0.0 |
| 832 |
*/ |
| 833 |
public function plugin_action_links( $links ): array { |
| 834 |
// IOK 2025-01-13 temporarily deactivated, because there is currently two |
| 835 |
// sets of settings, and not enough room to disambituate between them (at least in norwegian). |
| 836 |
return $links; |
| 837 |
|
| 838 |
$plugin_links = [ |
| 839 |
'<a href="admin.php?page=wc-settings&tab=checkout§ion=vipps_recurring">' . esc_html__( 'Settings', 'woo-vipps' ) . '</a>', |
| 840 |
]; |
| 841 |
|
| 842 |
return array_merge( $plugin_links, $links ); |
| 843 |
} |
| 844 |
|
| 845 |
/** |
| 846 |
* Handles upgrade routines. |
| 847 |
* |
| 848 |
* @since 3.1.0 |
| 849 |
* @version 3.1.0 |
| 850 |
*/ |
| 851 |
public function install() { |
| 852 |
$this->upgrade(); |
| 853 |
} |
| 854 |
|
| 855 |
/** |
| 856 |
* Add the gateways to WooCommerce. |
| 857 |
* |
| 858 |
* @param $methods |
| 859 |
* |
| 860 |
* @return array |
| 861 |
* @since 1.0.0 |
| 862 |
* @version 4.0.0 |
| 863 |
*/ |
| 864 |
public function add_gateways( $methods ): array { |
| 865 |
if ( function_exists( 'wcs_create_renewal_order' ) && class_exists( 'WC_Subscriptions_Order' ) ) { |
| 866 |
require_once( dirname( __FILE__ ) . "/wc-gateway-vipps-recurring.php" ); |
| 867 |
|
| 868 |
$methods[] = WC_Gateway_Vipps_Recurring::get_instance(); |
| 869 |
} |
| 870 |
|
| 871 |
return $methods; |
| 872 |
} |
| 873 |
|
| 874 |
/** |
| 875 |
* Enqueue our CSS and other assets. |
| 876 |
*/ |
| 877 |
public function wp_enqueue_scripts() { |
| 878 |
wp_enqueue_style( |
| 879 |
'woo-vipps-recurring', |
| 880 |
|
| 881 |
WC_VIPPS_RECURRING_PLUGIN_URL . '/assets/build/main.css', [], |
| 882 |
filemtime( WC_VIPPS_RECURRING_PLUGIN_PATH . '/assets/build/main.css' ) |
| 883 |
); |
| 884 |
|
| 885 |
$asset = require WC_VIPPS_RECURRING_PLUGIN_PATH . '/assets/build/main.asset.php'; |
| 886 |
|
| 887 |
wp_enqueue_script( |
| 888 |
'woo-vipps-recurring', |
| 889 |
WC_VIPPS_RECURRING_PLUGIN_URL . '/assets/build/main.js', |
| 890 |
$asset['dependencies'], |
| 891 |
filemtime( WC_VIPPS_RECURRING_PLUGIN_PATH . '/assets/build/main.js' ), |
| 892 |
true |
| 893 |
); |
| 894 |
|
| 895 |
$continue_shopping_link_page = ! empty( $this->gateway()->continue_shopping_link_page ) |
| 896 |
? $this->gateway()->continue_shopping_link_page |
| 897 |
: wc_get_page_id( 'shop' ); |
| 898 |
|
| 899 |
$continue_shopping_url = $continue_shopping_link_page ? get_permalink( $continue_shopping_link_page ) : home_url(); |
| 900 |
if ( empty( $continue_shopping_link_page ) ) { |
| 901 |
$continue_shopping_url = home_url(); |
| 902 |
} |
| 903 |
|
| 904 |
wp_localize_script( 'woo-vipps-recurring', 'VippsMobilePaySettings', [ |
| 905 |
'logo' => WC_VIPPS_RECURRING_PLUGIN_URL . '/assets/images/' . $this->gateway()->brand . '-logo.svg', |
| 906 |
'continueShoppingUrl' => $continue_shopping_url |
| 907 |
] ); |
| 908 |
|
| 909 |
wp_set_script_translations( 'woo-vipps-recurring', 'woo-vipps', dirname( WC_VIPPS_MAIN_FILE ) . '/languages' ); |
| 910 |
} |
| 911 |
|
| 912 |
/** |
| 913 |
* Enqueue our CSS and other assets. |
| 914 |
*/ |
| 915 |
public function admin_enqueue_scripts() { |
| 916 |
wp_enqueue_style( 'woo-vipps-recurring', plugins_url( 'assets/css/vipps-recurring-admin.css', WC_VIPPS_RECURRING_MAIN_FILE ), [], |
| 917 |
filemtime( dirname( WC_VIPPS_RECURRING_MAIN_FILE ) . '/assets/css/vipps-recurring-admin.css' ) ); |
| 918 |
|
| 919 |
$this->ajax_config['nonce'] = wp_create_nonce( 'vipps_recurring_ajax_nonce' ); |
| 920 |
$this->ajax_config['currency'] = get_woocommerce_currency(); |
| 921 |
|
| 922 |
wp_enqueue_script( |
| 923 |
'woo-vipps-recurring-admin', |
| 924 |
plugins_url( 'assets/js/vipps-recurring-admin.js', WC_VIPPS_RECURRING_MAIN_FILE ), |
| 925 |
[ 'wp-i18n' ], |
| 926 |
filemtime( dirname( WC_VIPPS_RECURRING_MAIN_FILE ) . "/assets/js/vipps-recurring-admin.js" ), |
| 927 |
true |
| 928 |
); |
| 929 |
|
| 930 |
wp_localize_script( 'woo-vipps-recurring-admin', 'VippsRecurringConfig', $this->ajax_config ); |
| 931 |
wp_set_script_translations( 'woo-vipps-recurring-admin', 'woo-vipps', dirname( WC_VIPPS_MAIN_FILE ) . '/languages' ); |
| 932 |
} |
| 933 |
|
| 934 |
/** |
| 935 |
* @param $schedules |
| 936 |
* |
| 937 |
* @return mixed |
| 938 |
*/ |
| 939 |
public function woocommerce_vipps_recurring_add_cron_schedules( $schedules ) { |
| 940 |
$schedules['one_minute'] = [ |
| 941 |
'interval' => 60, |
| 942 |
'display' => esc_html__( 'Every One Minute' ), |
| 943 |
]; |
| 944 |
|
| 945 |
return $schedules; |
| 946 |
} |
| 947 |
|
| 948 |
/** |
| 949 |
* @throws WC_Vipps_Recurring_Exception |
| 950 |
* @throws WC_Vipps_Recurring_Temporary_Exception |
| 951 |
* @throws WC_Vipps_Recurring_Config_Exception |
| 952 |
*/ |
| 953 |
public function handle_webhook_callback() { |
| 954 |
wc_nocache_headers(); |
| 955 |
status_header( 202, "Accepted" ); |
| 956 |
|
| 957 |
$raw_input = @file_get_contents( 'php://input' ); |
| 958 |
$body = @json_decode( $raw_input, true ); |
| 959 |
|
| 960 |
// If we have a sessionId, this indicates that we are dealing with a Checkout callback. |
| 961 |
// Fire a hook and let Checkout deal with its own logic. |
| 962 |
if ( isset( $body['sessionId'] ) ) { |
| 963 |
$authorization_token = $_SERVER['HTTP_AUTHORIZATION']; |
| 964 |
|
| 965 |
do_action( 'wc_vipps_recurring_checkout_callback', $body, $authorization_token ); |
| 966 |
|
| 967 |
// Early return to avoid potential errors downstream. |
| 968 |
return; |
| 969 |
} |
| 970 |
|
| 971 |
$callback = $_REQUEST['callback'] ?? ""; |
| 972 |
|
| 973 |
if ( $callback !== "webhook" ) { |
| 974 |
return; |
| 975 |
} |
| 976 |
|
| 977 |
if ( ! $body ) { |
| 978 |
$error = json_last_error_msg(); |
| 979 |
WC_Vipps_Recurring_Logger::log( sprintf( "Did not understand callback from Vipps/MobilePay with body: %s – error: %s", empty( $raw_input ) ? "(empty string)" : $raw_input, $error ) ); |
| 980 |
|
| 981 |
return; |
| 982 |
} |
| 983 |
|
| 984 |
$local_webhooks = $this->gateway()->webhook_get_local()[ $this->gateway()->merchant_serial_number ]; |
| 985 |
$local_webhook = array_pop( $local_webhooks ); |
| 986 |
$secret = $local_webhook ? ( $local_webhook['secret'] ?? false ) : false; |
| 987 |
|
| 988 |
$order_id = $body['chargeId'] ?? $body['agreementId']; |
| 989 |
if ( ! $order_id ) { |
| 990 |
WC_Vipps_Recurring_Logger::log( sprintf( "Could not find order id in webhook with body %s", json_encode( $body ) ) ); |
| 991 |
|
| 992 |
return; |
| 993 |
} |
| 994 |
|
| 995 |
if ( ! $secret ) { |
| 996 |
WC_Vipps_Recurring_Logger::log( sprintf( "Cannot verify webhook callback for order %s - this shop does not know the secret. You should delete all unwanted webhooks. If you are using the same MSN on several shops, this callback is probably for one of the others.", $order_id ) ); |
| 997 |
} |
| 998 |
|
| 999 |
if ( ! $this->verify_webhook( $raw_input, $secret ) ) { |
| 1000 |
return; |
| 1001 |
} |
| 1002 |
|
| 1003 |
do_action( "wc_vipps_recurring_webhook_callback", $body, $raw_input ); |
| 1004 |
|
| 1005 |
// We now have a validated webhook |
| 1006 |
$this->gateway()->handle_webhook_callback( $body ); |
| 1007 |
} |
| 1008 |
|
| 1009 |
public function verify_webhook( $serialized, $secret ): bool { |
| 1010 |
$expected_auth = $_SERVER['HTTP_AUTHORIZATION'] ?? ( $_SERVER['HTTP_X_VIPPS_AUTHORIZATION'] ?? "" ); |
| 1011 |
$expected_date = $_SERVER['HTTP_X_MS_DATE'] ?? ''; |
| 1012 |
|
| 1013 |
$hashed_payload = base64_encode( hash( 'sha256', $serialized, true ) ); |
| 1014 |
$path_and_query = $_SERVER['REQUEST_URI']; |
| 1015 |
$host = $_SERVER['HTTP_HOST']; |
| 1016 |
$toSign = "POST\n{$path_and_query}\n$expected_date;$host;$hashed_payload"; |
| 1017 |
$signature = base64_encode( hash_hmac( 'sha256', $toSign, $secret, true ) ); |
| 1018 |
$auth = "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=$signature"; |
| 1019 |
|
| 1020 |
return ( $auth == $expected_auth ); |
| 1021 |
} |
| 1022 |
|
| 1023 |
public function generate_order_prefix(): string { |
| 1024 |
$parts = parse_url( site_url() ); |
| 1025 |
if ( ! $parts ) { |
| 1026 |
return 'woo-'; |
| 1027 |
} |
| 1028 |
|
| 1029 |
$domain = explode( ".", $parts['host'] ?? '' ); |
| 1030 |
if ( empty( $domain ) ) { |
| 1031 |
return 'woo-'; |
| 1032 |
} |
| 1033 |
|
| 1034 |
$first = strtolower( $domain[0] ); |
| 1035 |
$second = $domain[1] ?? ''; |
| 1036 |
|
| 1037 |
// Select first part of domain unless that has no content, otherwise second. Default to "woo-" again. |
| 1038 |
$key = $first; |
| 1039 |
if ( in_array( $first, [ 'www', 'test', 'dev', 'vdev' ] ) && ! empty( $second ) ) { |
| 1040 |
$key = $second; |
| 1041 |
} |
| 1042 |
|
| 1043 |
// Use only 8 chars for the site. Try to make it so by dropping vowels, if that doesn't succeed, just chop it. |
| 1044 |
$key = sanitize_title( $key ); |
| 1045 |
$len = strlen( $key ); |
| 1046 |
if ( $len <= 8 ) { |
| 1047 |
return "woo-$key-"; |
| 1048 |
} |
| 1049 |
|
| 1050 |
$kzk = preg_replace( "/[aeiouæøåüö]/i", "", $key ); |
| 1051 |
if ( strlen( $kzk ) <= 8 ) { |
| 1052 |
return "woo-$kzk-"; |
| 1053 |
} |
| 1054 |
|
| 1055 |
return "woo-" . substr( $key, 0, 8 ) . "-"; |
| 1056 |
} |
| 1057 |
} |
| 1058 |
|