@@ -147,7 +147,8 @@ const CheckoutOneClick = () => {
147147
148148 // Form for billing address
149149 const billingAddressForm = useForm ( {
150- mode : 'onChange' ,
150+ mode : 'onTouched' ,
151+ reValidateMode : 'onChange' ,
151152 shouldUnregister : false ,
152153 defaultValues : { ...selectedBillingAddress }
153154 } )
@@ -200,17 +201,66 @@ const CheckoutOneClick = () => {
200201 let billingAddress
201202 if ( billingSameAsShipping && selectedShippingAddress ) {
202203 billingAddress = selectedShippingAddress
204+ // Validate that shipping address has required address fields
205+ if ( ! billingAddress ?. address1 ) {
206+ showError (
207+ formatMessage ( {
208+ id : 'checkout.error.billing_address_required' ,
209+ defaultMessage : 'Please enter a billing address.'
210+ } )
211+ )
212+ return
213+ }
203214 } else {
204- const isFormValid = await billingAddressForm . trigger ( )
215+ // Validate all required address fields (excluding phone for billing)
216+ const fieldsToValidate = [
217+ 'address1' ,
218+ 'firstName' ,
219+ 'lastName' ,
220+ 'city' ,
221+ 'stateCode' ,
222+ 'postalCode' ,
223+ 'countryCode'
224+ ]
225+
226+ // First, mark all fields as touched so errors will be displayed when validation runs
227+ // This must happen BEFORE trigger() so errors show immediately
228+ fieldsToValidate . forEach ( ( field ) => {
229+ const currentValue = billingAddressForm . getValues ( field ) || ''
230+ billingAddressForm . setValue ( field , currentValue , {
231+ shouldValidate : false ,
232+ shouldTouch : true
233+ } )
234+ } )
235+
236+ // Now trigger validation - errors will show because fields are already touched
237+ const isFormValid = await billingAddressForm . trigger ( fieldsToValidate )
205238
206239 if ( ! isFormValid ) {
240+ // Payment section should already be open from onPlaceOrder
241+ // Focus on the first name field (first field in the form)
242+ setTimeout ( ( ) => {
243+ billingAddressForm . setFocus ( 'firstName' )
244+ } , 100 )
207245 return
208246 }
209247 billingAddress = billingAddressForm . getValues ( )
248+
249+ // Double-check that address is present
250+ if ( ! billingAddress ?. address1 ) {
251+ showError (
252+ formatMessage ( {
253+ id : 'checkout.error.billing_address_required' ,
254+ defaultMessage : 'Please enter a billing address.'
255+ } )
256+ )
257+ setIsEditingPayment ( true )
258+ return
259+ }
210260 }
211261
212262 // eslint-disable-next-line @typescript-eslint/no-unused-vars
213- const { addressId, creationDate, lastModified, preferred, ...address } = billingAddress
263+ const { addressId, creationDate, lastModified, preferred, phone , ...address } = billingAddress
214264 const latestBasketId = currentBasketQuery . data ?. basketId || basket . basketId
215265 return await updateBillingAddressForBasket ( {
216266 body : address ,
@@ -370,15 +420,20 @@ const CheckoutOneClick = () => {
370420 }
371421 } )
372422 }
423+ }
373424
374- // Also persist billing phone as phoneHome
375- const phoneHome = order ?. billingAddress ?. phone
376- if ( phoneHome ) {
377- await updateCustomer . mutateAsync ( {
378- parameters : { customerId} ,
379- body : { phoneHome}
380- } )
381- }
425+ // Persist phone number as phoneHome from shipping address or basket
426+ // Since billing address no longer has phone, get it from shipping address
427+ // For delivery orders, use shipping address phone; for pickup-only, use basket customerInfo phone
428+ const phoneHome =
429+ deliveryShipments . length > 0
430+ ? deliveryShipments [ 0 ] ?. shippingAddress ?. phone
431+ : basket ?. customerInfo ?. phone
432+ if ( phoneHome ) {
433+ await updateCustomer . mutateAsync ( {
434+ parameters : { customerId} ,
435+ body : { phoneHome}
436+ } )
382437 }
383438 } catch ( _e ) {
384439 // Only surface error if shopper opted to register/save details; otherwise fail silently
@@ -442,6 +497,13 @@ const CheckoutOneClick = () => {
442497 }
443498 }
444499
500+ // Ensure payment section is open before validating billing address
501+ // This ensures the billing form is rendered and visible when we validate
502+ setIsEditingPayment ( true )
503+
504+ // Wait for the payment section to open and billing form to render
505+ await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) )
506+
445507 // If successful `onBillingSubmit` returns the updated basket. If the form was invalid on
446508 // submit, `undefined` is returned.
447509 const updatedBasket = await onBillingSubmit ( )
0 commit comments