Automattic\WooCommerce\StoreApi\Routes\V1

Checkout::validate_order_totalsprivateWC 1.0

Reject the order if what the customer is going to be charged at is higher from what they saw.

Runs on POST /checkout only, before the draft order is materialised, so a mismatch leaves no order behind. The check only runs when the client sends the total it displayed; flows that cannot know the final total up front (e.g. some express payment methods) omit it and are unaffected.

Method of the class: Checkout{}

No Hooks.

Returns

null. Nothing (null).

Usage

// private - for code of main (parent) class only
$result = $this->validate_order_totals( $request ): void;
$request(WP_REST_Request) (required)
Request object.

Checkout::validate_order_totals() code WC 11.1.2

private function validate_order_totals( \WP_REST_Request $request ): void {
	// The route schema constrains this to minor-unit digits, so the integer comparison below is exact.
	$expected_total = (string) ( $request['expected_total'] ?? '' );

	if ( '' === $expected_total ) {
		return;
	}

	// CartSchema::prepare_money_response() is not exported, so we use the money formatter directly here to match the total_price format.
	$decimals     = wc_get_price_decimals();
	$actual_total = woocommerce_store_api_get_formatter( 'money' )->format(
		$this->cart_controller->get_cart_instance()->get_total( 'edit' ),
		[ 'decimals' => $decimals ]
	);

	if ( (int) $actual_total <= (int) $expected_total ) {
		return;
	}

	$expected_amount = wc_remove_number_precision( absint( $expected_total ) );
	$actual_amount   = wc_remove_number_precision( absint( $actual_total ) );

	throw new RouteException(
		'woocommerce_rest_checkout_total_mismatch',
		sprintf(
			/* translators: %1$s: total the shopper confirmed, %2$s: updated, higher total */
			esc_html__( 'The order total changed from %1$s to %2$s while you were checking out. Please review the updated total and place your order again.', 'woocommerce' ),
			esc_html( wc_price( $expected_amount, [ 'in_span' => false ] ) ),
			esc_html( wc_price( $actual_amount, [ 'in_span' => false ] ) )
		),
		409,
		[
			'expected_total' => absint( $expected_total ),
			'actual_total'   => absint( $actual_total ),
		]
	);
}