Commit c88d5dc4296 for woocommerce
commit c88d5dc4296fd0160df52b0ef9a19c2ef4847dec
Author: Raluca Stan <ralucastn@gmail.com>
Date: Mon Jul 27 07:20:39 2026 -0400
Store API: guard checkout against totals changing during the request (#66420)
* Store API: guard checkout against totals changing during the request
Reject POST /checkout with a 409 when the total the shopper confirmed no
longer matches the total the server calculates for the request, so shoppers
aren't charged an unexpected amount when something changes mid-checkout
(price/qty edits, stock clamps, coupon expiry, shipping/tax recalculation).
Opt-in via a new optional `expected_total` field; the block checkout sends
it and skips it for express payment methods. Follow-up to #52154.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Docs: fix markdown table alignment (MD060)
Keep the `expected_total` cell within the table's column width and move
the full description to a note below the table.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Compare checkout total via money formatter, add free-order test
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add missing docblock to free-order expected_total test
Fixes phpcs Squiz.Commenting.FunctionComment.Missing on the branch lint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add expected_total to existing checkout POST tests
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Seghir Nadir <nadir.seghir@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
diff --git a/docs/apis/store-api/resources-endpoints/checkout.md b/docs/apis/store-api/resources-endpoints/checkout.md
index 243afdbcd81..e75c7ec3a73 100644
--- a/docs/apis/store-api/resources-endpoints/checkout.md
+++ b/docs/apis/store-api/resources-endpoints/checkout.md
@@ -157,6 +157,9 @@ POST /wc/store/v1/checkout
| `payment_method` | string | Yes | The ID of the payment method being used to process the payment. |
| `payment_data` | array | No | Data to pass through to the payment method when processing payment. |
| `customer_password` | string | No | Optionally define a password for new accounts. |
+| `expected_total` | string | No | Total the shopper confirmed, in minor units. See note below. |
+
+`expected_total` is a string in the smallest unit of the store currency (e.g. cents), matching the cart `totals.total_price` format. When provided, the order is rejected with a `409` (`woocommerce_rest_checkout_total_mismatch`) if the total the server calculates for the request no longer matches it, and the refreshed cart is returned so the client can display the updated total. Omit it to skip the check — e.g. for flows that cannot know the final total up front, such as some express payment methods.
```sh
curl --header "Nonce: 12345" --request POST https://example-store.com/wp-json/wc/store/v1/checkout?payment_method=paypal&payment_data[0][key]=test-key&payment_data[0][value]=test-value
diff --git a/plugins/woocommerce/changelog/fix-checkout-total-race-guard b/plugins/woocommerce/changelog/fix-checkout-total-race-guard
new file mode 100644
index 00000000000..fa455661ab3
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-checkout-total-race-guard
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Store API/Checkout Block: reject the order with a 409 when the total the shopper confirmed no longer matches the total calculated on the server (e.g. a product, price or quantity changed during checkout), so shoppers are never charged an unexpected amount.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/providers/cart-checkout/checkout-processor.ts b/plugins/woocommerce/client/blocks/assets/js/base/context/providers/cart-checkout/checkout-processor.ts
index c9e815a3777..c2f3c6679ab 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/providers/cart-checkout/checkout-processor.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/providers/cart-checkout/checkout-processor.ts
@@ -89,8 +89,12 @@ const CheckoutProcessor = () => {
const { shippingAddress, billingAddress, useBillingAsShipping } =
useCheckoutAddress();
- const { cartNeedsPayment, cartNeedsShipping, receiveCartContents } =
- useStoreCart();
+ const {
+ cartNeedsPayment,
+ cartNeedsShipping,
+ cartTotals,
+ receiveCartContents,
+ } = useStoreCart();
const {
activePaymentMethod,
@@ -270,6 +274,15 @@ const CheckoutProcessor = () => {
shipping_address: cartNeedsShipping
? shippingAddressData
: undefined,
+ // Send the total the shopper is currently seeing so the server can reject the
+ // order if it no longer matches (e.g. a product, price or quantity changed during
+ // checkout). Express payment methods may not know the final total up front, so
+ // they opt out of this check.
+ ...( ! isExpressPaymentMethodActive &&
+ cartTotals &&
+ cartTotals.total_price !== ''
+ ? { expected_total: cartTotals.total_price }
+ : {} ),
...paymentData,
};
@@ -337,6 +350,8 @@ const CheckoutProcessor = () => {
}, [
isProcessingOrder,
cartNeedsPayment,
+ cartTotals,
+ isExpressPaymentMethodActive,
paymentMethodId,
paymentMethodData,
shouldSavePayment,
diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php
index ea3a6c15039..6d9fb42ef95 100644
--- a/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php
+++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php
@@ -107,6 +107,10 @@ class Checkout extends AbstractCartRoute {
'description' => __( 'Customer password for new accounts, if applicable.', 'woocommerce' ),
'type' => 'string',
],
+ 'expected_total' => [
+ 'description' => __( 'The order total the shopper confirmed on the client, as a string in the smallest unit of the store currency (e.g. cents), matching the cart `totals.total_price` format. When provided, the order is rejected if the total calculated on the server no longer matches it, protecting the shopper from being charged an unexpected amount.', 'woocommerce' ),
+ 'type' => 'string',
+ ],
],
$this->schema->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE )
),
@@ -561,6 +565,14 @@ class Checkout extends AbstractCartRoute {
*/
$this->cart_controller->validate_cart();
+ /**
+ * Reject the order if the total the shopper confirmed no longer matches the total the
+ * server calculates for this request (e.g. a server-side filter or another concurrent
+ * request changed the cart, a product price/quantity was edited, a stock limit clamped
+ * a quantity, a coupon expired, or shipping/tax was recalculated).
+ */
+ $this->validate_order_totals( $request );
+
/**
* Persist customer session data from the request first so that OrderController::update_addresses_from_cart
* uses the up-to-date customer address.
@@ -1045,6 +1057,44 @@ class Checkout extends AbstractCartRoute {
return false;
}
+ /**
+ * Reject the order if the total the shopper confirmed on the client no longer matches the
+ * total the server recalculates for this place-order request. Without this guard the order
+ * could be placed — and the shopper charged — at a total they never 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.
+ *
+ * @phpstan-param \WP_REST_Request<array<string, mixed>> $request
+ *
+ * @param \WP_REST_Request $request Request object.
+ * @throws RouteException When the totals differ. Returns HTTP 409 with the refreshed cart so the client can display the updated total.
+ */
+ private function validate_order_totals( \WP_REST_Request $request ): void {
+ $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 ( $expected_total !== $actual_total ) {
+ throw new RouteException(
+ 'woocommerce_rest_checkout_total_mismatch',
+ esc_html__( 'The order total changed while you were checking out. Please review the updated total and place your order again.', 'woocommerce' ),
+ 409
+ );
+ }
+ }
+
/**
* This validates if the order can be placed regarding settings in WooCommerce > Settings > Accounts & Privacy
* If registration during checkout is disabled, guest checkout is disabled and the user is not logged in, prevent checkout.
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/OrderConfirmation/Totals.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/OrderConfirmation/Totals.php
index 2ce61c4d8a7..64ac1528a3a 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/OrderConfirmation/Totals.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/OrderConfirmation/Totals.php
@@ -174,6 +174,7 @@ class Totals extends \WP_UnitTestCase {
'create_account' => true,
'customer_note' => '<a href="http://attackerpage.com/csrf.html">This text should not save inside an anchor.</a><script>alert("alert")</script>',
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => true,
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
index 9c3e38847bc..0761670c70e 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
@@ -290,6 +290,185 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
+ )
+ );
+ $response = rest_get_server()->dispatch( $request );
+ $this->assertEquals( 200, $response->get_status(), print_r( $response->get_data(), true ) );
+ }
+
+ /**
+ * Ensure an order is placed when the expected total sent by the client matches the server total.
+ */
+ public function test_post_data_accepts_matching_expected_total() {
+ WC()->cart->calculate_totals();
+ $expected_total = (string) (int) round( (float) WC()->cart->get_total( 'edit' ) * pow( 10, wc_get_price_decimals() ), 0, PHP_ROUND_HALF_UP );
+
+ $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' );
+ $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) );
+ $request->set_body_params(
+ array(
+ 'billing_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ 'email' => 'testaccount@test.com',
+ ),
+ 'shipping_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ ),
+ 'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => $expected_total,
+ )
+ );
+ $response = rest_get_server()->dispatch( $request );
+ $this->assertEquals( 200, $response->get_status(), print_r( $response->get_data(), true ) );
+ }
+
+ /**
+ * Ensure an order is rejected with a 409 when the expected total sent by the client no longer
+ * matches the total calculated on the server, and the refreshed cart is returned.
+ */
+ public function test_post_data_rejects_mismatched_expected_total() {
+ $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' );
+ $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) );
+ $request->set_body_params(
+ array(
+ 'billing_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ 'email' => 'testaccount@test.com',
+ ),
+ 'shipping_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ ),
+ 'payment_method' => WC_Gateway_BACS::ID,
+ // A total the customer could never have seen for this cart.
+ 'expected_total' => '1',
+ )
+ );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+
+ $this->assertEquals( 409, $response->get_status(), print_r( $data, true ) );
+ $this->assertEquals( 'woocommerce_rest_checkout_total_mismatch', $data['code'] );
+ $this->assertArrayHasKey( 'cart', $data['data'], 'The refreshed cart should be returned so the client can display the updated total.' );
+ }
+
+ /**
+ * Ensure the total check is skipped (order placed) when the client does not send an expected total.
+ */
+ public function test_post_data_skips_total_check_when_not_provided() {
+ $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' );
+ $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) );
+ $request->set_body_params(
+ array(
+ 'billing_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ 'email' => 'testaccount@test.com',
+ ),
+ 'shipping_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ ),
+ 'payment_method' => WC_Gateway_BACS::ID,
+ )
+ );
+ $response = rest_get_server()->dispatch( $request );
+ $this->assertEquals( 200, $response->get_status(), print_r( $response->get_data(), true ) );
+ }
+
+ /**
+ * Ensure a free order (zero total) is accepted when the client sends a matching expected total.
+ */
+ public function test_post_data_accepts_matching_expected_total_for_free_order() {
+ $fixtures = new FixtureData();
+ $free_product = $fixtures->get_simple_product(
+ array(
+ 'name' => 'Free Test Product',
+ 'stock_status' => ProductStockStatus::IN_STOCK,
+ 'regular_price' => 0,
+ 'virtual' => true,
+ )
+ );
+
+ wc_empty_cart();
+ wc()->cart->add_to_cart( $free_product->get_id(), 1 );
+ wc()->cart->calculate_totals();
+
+ // The cart is genuinely free, and a zero total serialises to the string "0".
+ $expected_total = (string) (int) round( (float) WC()->cart->get_total( 'edit' ) * pow( 10, wc_get_price_decimals() ), 0, PHP_ROUND_HALF_UP );
+ $this->assertSame( '0', $expected_total );
+
+ $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' );
+ $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) );
+ $request->set_body_params(
+ array(
+ 'billing_address' => (object) array(
+ 'first_name' => 'test',
+ 'last_name' => 'test',
+ 'company' => '',
+ 'address_1' => 'test',
+ 'address_2' => '',
+ 'city' => 'test',
+ 'state' => '',
+ 'postcode' => 'cb241ab',
+ 'country' => 'GB',
+ 'phone' => '1234567890',
+ 'email' => 'testaccount@test.com',
+ ),
+ 'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => $expected_total,
)
);
$response = rest_get_server()->dispatch( $request );
@@ -354,6 +533,7 @@ class Checkout extends \WP_Test_REST_TestCase {
array(
'billing_address' => (object) $this->get_fallback_billing_address(),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
@@ -384,6 +564,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'billing_address' => (object) $this->get_fallback_billing_address(),
'shipping_address' => (object) $this->get_fallback_shipping_address(),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
@@ -412,6 +593,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'billing_address' => (object) $this->get_fallback_billing_address(),
'shipping_address' => (object) $this->get_fallback_shipping_address(),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '1000',
)
);
@@ -460,6 +642,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '4000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -500,6 +683,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => 'apples',
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -544,6 +728,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -593,6 +778,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -641,6 +827,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '2800',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -678,6 +865,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '2800',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -721,6 +909,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '2800',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -764,6 +953,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -798,6 +988,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -830,6 +1021,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -888,6 +1080,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '123456',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -942,6 +1135,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '123456',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -1000,6 +1194,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => true,
@@ -1058,6 +1253,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => 'invalid-string',
@@ -1104,6 +1300,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'other_extension_data' => array(
'another_key' => true,
@@ -1150,6 +1347,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
@@ -1195,6 +1393,7 @@ class Checkout extends \WP_Test_REST_TestCase {
),
'create_account' => true,
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => true,
@@ -1253,6 +1452,7 @@ class Checkout extends \WP_Test_REST_TestCase {
),
'create_account' => false,
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => true,
@@ -1310,6 +1510,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => true,
@@ -1365,6 +1566,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
@@ -1422,6 +1624,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '4000',
)
);
@@ -1475,6 +1678,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
'extensions' => array(
'extension_namespace' => array(
'extension_key' => true,
@@ -1590,6 +1794,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/job-function' => 'engineering',
'plugin-namespace/leave-on-porch' => true,
),
+ 'expected_total' => '3000',
)
);
@@ -1718,6 +1923,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/student-id' => '12345678',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '4000',
)
);
@@ -1763,6 +1969,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/student-id' => '12345678',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '4000',
)
);
@@ -1919,6 +2126,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/student-id' => '12345678',
),
'payment_method' => WC_Gateway_BACS::ID, // Payment method might still be required, even if free.
+ 'expected_total' => '0',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -1971,6 +2179,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/student-id' => '12345678',
),
'payment_method' => '',
+ 'expected_total' => '0',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -2036,6 +2245,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/student-id' => '12345678',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '1000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -2102,6 +2312,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'plugin-namespace/student-id' => '12345678',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '1000',
)
);
$response = rest_get_server()->dispatch( $request );
@@ -2219,6 +2430,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '5555555555',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
@@ -2575,6 +2787,7 @@ class Checkout extends \WP_Test_REST_TestCase {
'phone' => '5555555555',
),
'payment_method' => WC_Gateway_BACS::ID,
+ 'expected_total' => '3000',
)
);
return $request;