Skip to content

Commit 01876f5

Browse files
authored
feat: persist shipping phone through the checkout mutation (#1017)
* feat: persist shipping phone through the checkout mutation The shipping fieldset in Checkout_Mutation::get_checkout_fields() omitted the phone field, so a phone passed in the checkout mutation's shipping input was never mapped into shipping_phone and WC_Order::set_shipping_phone() was never called. Add phone to the shipping fieldset so it flows through to the order, matching the billing fieldset. Closes #1016 * fix: load QL session handler when a session token header is present should_load_session_handler() only matched explicit wc-ajax / WC_DOING_AJAX and REST_REQUEST contexts. Headless callers drive session state through the Store-API Cart-Token header or the legacy woocommerce-session header and can land on other admin-ajax/REST entrypoints, leaving the session bootstrapped from an absent cookie. Detect either session header and also honor wp_doing_ajax() so QL_Session_Handler loads and rebuilds the session from the token. * ci: authenticate composer GitHub access to fix flaky strauss install The dependency-install steps run with the github-oauth token that composer rejects ("contains invalid characters"), so composer and strauss's internal composer bootstrap fall back to unauthenticated GitHub access. Under the shared runner IP's 60/hr unauthenticated rate limit, strauss intermittently exits 1 during post-install-cmd, failing lint, PHPStan, wpunit, functional and acceptance jobs at random. Provide a well-formed COMPOSER_AUTH built from the always-available GITHUB_TOKEN on every dependency-install step so GitHub API access is authenticated (5000/hr), removing the flake. * ci: run strauss with a clean composer auth context setup-php writes the runner's GITHUB_TOKEN into composer's global auth.json, and this composer rejects that token format ("github oauth token contains invalid characters"). strauss spins up its own internal Composer instance, which reads that auth, hits the validation error and exits 1 with no output, failing every job at the post-install/post-update strauss step. strauss only rewrites local vendor packages and needs no GitHub auth, so run it with COMPOSER_AUTH unset and a throwaway COMPOSER_HOME. Fixes all CI install steps, local installs and release packaging from one place. This reverts the earlier per-workflow COMPOSER_AUTH attempt, which could not work because composer rejects the token regardless of how it is supplied. * style: align equals sign in should_load_session_handler (phpcs)
1 parent e82ace9 commit 01876f5

4 files changed

Lines changed: 68 additions & 3 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"stan": "phpstan analyze --ansi --memory-limit=4G",
7676
"strauss": [
7777
"test -f ./bin/strauss.phar || curl -o bin/strauss.phar -L -C - https://github.com/BrianHenryIE/strauss/releases/download/0.14.0/strauss.phar",
78-
"@php bin/strauss.phar",
78+
"env -u COMPOSER_AUTH COMPOSER_HOME=$(mktemp -d) php bin/strauss.phar",
7979
"composer dump-autoload --optimize"
8080
],
8181
"cghooks": [

includes/class-woocommerce.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,36 @@ public static function get_authorizing_url_nonce_param_name( $field ) {
124124
* @return boolean
125125
*/
126126
public static function should_load_session_handler() {
127+
// Any request carrying either the Store-API Cart-Token header or the
128+
// legacy `woocommerce-session` (filterable) header is a headless
129+
// caller driving session state through the token, regardless of
130+
// which WP endpoint it lands on (admin-ajax, REST, the front-end,
131+
// etc.). We need QL_Session_Handler here too so the session is
132+
// bootstrapped from the token instead of the (absent) WC session
133+
// cookie.
134+
$legacy_header_key = 'HTTP_' . strtoupper(
135+
preg_replace(
136+
'#[^A-z0-9]#',
137+
'_',
138+
apply_filters( 'graphql_woocommerce_cart_session_http_header', 'woocommerce-session' )
139+
)
140+
);
141+
$has_session_header = ! empty( $_SERVER['HTTP_CART_TOKEN'] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
142+
|| ! empty( $_SERVER[ $legacy_header_key ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
143+
127144
switch ( true ) {
128145
case \WPGraphQL\Router::is_graphql_http_request():
129146
//phpcs:disable
130147
case 'on' === woographql_setting( 'enable_ql_session_handler_on_ajax', 'off' )
131-
&& ( ! empty( $_GET['wc-ajax'] ) || defined( 'WC_DOING_AJAX' ) ):
148+
&& (
149+
! empty( $_GET['wc-ajax'] )
150+
|| defined( 'WC_DOING_AJAX' )
151+
|| wp_doing_ajax()
152+
|| $has_session_header
153+
):
132154
//phpcs:enable
133155
case 'on' === woographql_setting( 'enable_ql_session_handler_on_rest', 'off' )
134-
&& ( defined( 'REST_REQUEST' ) && REST_REQUEST ):
156+
&& ( ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || $has_session_header ):
135157
return true;
136158
default:
137159
return false;

includes/data/mutation/class-checkout-mutation.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ public static function get_checkout_fields( $fieldset = '', $prefixed = false )
164164
'postcode' => 'postcode',
165165
'state' => 'state',
166166
'country' => 'country',
167+
'phone' => 'phone',
167168
],
168169
'account' => [
169170
'username' => 'username',

tests/wpunit/CheckoutMutationTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ private function getCheckoutMutation() {
133133
state
134134
postcode
135135
country
136+
phone
136137
}
137138
paymentMethod
138139
paymentMethodTitle
@@ -293,6 +294,7 @@ private function getCheckoutInput( $overwrite = [] ) {
293294
'state' => 'NY',
294295
'postcode' => '12345',
295296
'country' => 'US',
297+
'phone' => '555-555-6789',
296298
],
297299
'metaData' => [
298300
[
@@ -446,6 +448,8 @@ public function testCheckoutMutation() {
446448
'checkout.customer.id',
447449
$this->toRelayId( 'user', $this->customer )
448450
),
451+
$this->expectedField( 'checkout.order.billing.phone', '555-555-1234' ),
452+
$this->expectedField( 'checkout.order.shipping.phone', '555-555-6789' ),
449453
$this->expectedField( 'checkout.result', 'success' ),
450454
$this->expectedField( 'checkout.redirect', static::NOT_NULL ),
451455
];
@@ -463,6 +467,44 @@ public function testCheckoutMutation() {
463467
$this->assertQuerySuccessful( $response, $expected );
464468
}
465469

470+
public function testCheckoutMutationPersistsShippingPhone() {
471+
$this->loginAsCustomer();
472+
473+
$product_id = $this->factory->product->createSimple();
474+
WC()->cart->add_to_cart( $product_id, 1 );
475+
476+
$input = [
477+
'shipping' => [
478+
'firstName' => 'May',
479+
'lastName' => 'Parker',
480+
'address1' => '20 Ingram St',
481+
'city' => 'New York City',
482+
'state' => 'NY',
483+
'postcode' => '12345',
484+
'country' => 'US',
485+
'phone' => '555-555-6789',
486+
],
487+
];
488+
$variables = [ 'input' => $this->getCheckoutInput( $input ) ];
489+
$query = $this->getCheckoutMutation();
490+
491+
$response = $this->graphql( compact( 'query', 'variables' ) );
492+
493+
// The shipping phone is returned on the order and is distinct from the billing phone.
494+
$this->assertQuerySuccessful(
495+
$response,
496+
[
497+
$this->expectedField( 'checkout.order.billing.phone', '555-555-1234' ),
498+
$this->expectedField( 'checkout.order.shipping.phone', '555-555-6789' ),
499+
]
500+
);
501+
502+
// And it is saved on the underlying WC_Order object.
503+
$order_id = $response['data']['checkout']['order']['databaseId'];
504+
$order = \wc_get_order( $order_id );
505+
$this->assertEquals( '555-555-6789', $order->get_shipping_phone() );
506+
}
507+
466508
public function testCheckoutMutationWithNewAccount() {
467509
$variable = $this->factory->product_variation->createSome();
468510
$product_ids = [

0 commit comments

Comments
 (0)