<?php
/**
* Stripe Calculation Tracker.
*
* @package Stripe\StripeTaxForWooCommerce\Stripe
*/
namespace Stripe\StripeTaxForWooCommerce\Stripe;
// Exit if script started not from WordPress.
defined( 'ABSPATH' ) || exit;
use WP_Post;
/**
* Stripe Calculation Tracker service
*/
class StripeCalculationTracker {
/**
* Should calculate variable
*
* @var bool
*/
private static $should_calculate = false;
/**
* Subscribe to hooks
*
* @return void
*/
public static function init(): void {
add_action(
'rest_dispatch_request',
function ( $_, $request ) {
$requests = $request->get_param( 'requests' );
if ( ! empty( $requests ) ) {
$data = reset( $requests );
if ( array_key_exists( 'path', $data ) && strpos( $data['path'], 'cart' ) !== false ) {
self::$should_calculate = true;
}
}
},
10,
2
);
add_action(
'admin_init',
function () {
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
wp_verify_nonce( $nonce );
// @phpstan-ignore-next-line
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['action'] ) && 'woocommerce_calc_line_taxes' === $_REQUEST['action'] ) {
self::$should_calculate = true;
}
}
);
}
/**
* REST API endpoints that should always allow a Stripe tax calculation.
*
* @var string[]
*/
public static $valid_rest_urls = array(
// WooCommerce core REST endpoints commonly used by the mobile app.
'/wp-json/wc/v3/orders',
'/wp-json/wc/v2/orders',
'/wp-json/wc/v1/orders',
'/wc/v3/orders',
'/wc/v2/orders',
'/wc/v1/orders',
// Jetpack Tunnel endpoints for WordPress.com authentication.
'/?rest_route=%2Fwc%2Fv3%2Forders',
'/?rest_route=%2Fwc%2Fv2%2Forders',
'/?rest_route=%2Fwc%2Fv1%2Forders',
// Store API (Cart/Checkout) endpoints.
'/wp-json/wc/store/v1/checkout',
'/wp-json/wc/store/v1/cart',
'rest_route=/wc/store/v1/checkout',
'rest_route=%2Fwc%2Fstore%2Fv1%2Fcheckout',
'rest_route=/wc/store/v1/cart',
'rest_route=%2Fwc%2Fstore%2Fv1%2Fcart',
);
/**
* Get the allow-listed REST endpoints (filterable).
*
* @return string[]
*/
public static function get_valid_rest_urls(): array {
$urls = apply_filters( 'stripe_tax_valid_rest_urls', self::$valid_rest_urls );
return is_array( $urls ) ? $urls : array();
}
/**
* Check if a calculation is needed.
*
* @return bool
*/
public static function is_calculation_needed(): bool {
try {
if ( apply_filters( 'stripe_tax_skip_calculation', false ) ) {
return false;
}
if ( self::$should_calculate || is_cart() || is_checkout() ) {
return true;
}
if ( defined( 'REST_REQUEST' ) && REST_REQUEST && isset( $_SERVER['REQUEST_URI'] ) ) {
foreach ( self::$valid_rest_urls as $valid_rest_url ) {
if ( strpos( sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ), $valid_rest_url ) !== false ) {
return true;
}
}
}
$cart_page_id = wc_get_page_id( 'cart' );
$checkout_page_id = wc_get_page_id( 'checkout' );
if ( get_queried_object_id() === $cart_page_id || get_queried_object_id() === $checkout_page_id ) {
return true;
}
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
$request = wp_parse_url( sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ), PHP_URL_PATH );
$request = $request ? untrailingslashit( $request ) : '';
$cart_url = get_permalink( $cart_page_id );
$cart_path = wp_parse_url( $cart_url, PHP_URL_PATH );
$cart_path = $cart_path ? untrailingslashit( $cart_path ) : '';
// Avoid false positives on plain permalinks where cart path becomes '/'.
if ( $cart_path && '/' !== $cart_path && $request === $cart_path ) {
return true;
}
$checkout_url = get_permalink( $checkout_page_id );
$checkout_path = wp_parse_url( $checkout_url, PHP_URL_PATH );
$checkout_path = $checkout_path ? untrailingslashit( $checkout_path ) : '';
// Avoid false positives on plain permalinks where checkout path becomes '/'.
if ( $checkout_path && '/' !== $checkout_path && $request === $checkout_path ) {
return true;
}
}
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
wp_verify_nonce( $nonce );
if ( isset( $_POST['woocommerce-cart-nonce'] ) || isset( $_POST['woocommerce-process-checkout-nonce'] ) ) {
return true;
}
global $post;
if ( $post instanceof WP_Post ) {
if (
has_shortcode( $post->post_content, 'woocommerce_cart' ) ||
has_shortcode( $post->post_content, 'woocommerce_checkout' ) ||
strpos( $post->post_content, 'wp:woocommerce/cart' ) !== false ||
strpos( $post->post_content, 'wp:woocommerce/checkout' ) !== false
) {
return true;
}
}
return self::is_cart_ajax_context() || self::is_checkout_ajax_context() || self::is_order_received_page();
} catch ( \Throwable $e ) {
// TODO: add logger after issue #41 is done.
return false;
}
}
/**
* Check if calculation is needed in cart ajax context
*
* @return bool
*/
private static function is_cart_ajax_context(): bool {
$cart_ajax_actions = array(
'update_cart',
'apply_coupon',
'remove_coupon',
'get_cart_totals',
'get_refreshed_fragments',
'update_shipping_method',
'checkout_coupon',
);
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
wp_verify_nonce( $nonce );
// @phpstan-ignore-next-line
return defined( 'DOING_AJAX' ) && DOING_AJAX &&
isset( $_GET['wc-ajax'] ) &&
in_array( sanitize_text_field( $_GET['wc-ajax'] ), $cart_ajax_actions, true );
}
/**
* Check if calculation is needed in checkout ajax context
*
* @return bool
*/
private static function is_checkout_ajax_context(): bool {
$checkout_ajax_actions = array(
'update_order_review',
'checkout',
'apply_coupon',
'remove_coupon',
'get_refreshed_fragments',
'update_shipping_method',
'checkout_coupon',
// Stripe gateway updates Checkout Session / PaymentIntent through this endpoint.
'wc_stripe_update_checkout_session',
);
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
wp_verify_nonce( $nonce );
// @phpstan-ignore-next-line
return defined( 'DOING_AJAX' ) && DOING_AJAX &&
isset( $_GET['wc-ajax'] ) &&
in_array( sanitize_text_field( $_GET['wc-ajax'] ), $checkout_ajax_actions, true );
}
/**
* Check if calculation is needed after clicking place order
*
* @return bool
*/
private static function is_order_received_page(): bool {
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
wp_verify_nonce( $nonce );
return is_page( wc_get_page_id( 'checkout' ) ) &&
isset( $_GET['key'] ) &&
isset( $_GET['order-received'] );
}
}