diff --git a/js/src/components/contact-information/index.js b/js/src/components/contact-information/index.js
index 3d8665779e..a519723dfd 100644
--- a/js/src/components/contact-information/index.js
+++ b/js/src/components/contact-information/index.js
@@ -82,24 +82,13 @@ export function ContactInformationPreview() {
* Renders a contact information section with specified initial state and texts.
*
* @param {Object} props React props.
- * @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified.
+ * @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified or has been verified.
* @fires gla_documentation_link_click with `{ context: 'setup-mc-contact-information', link_id: 'contact-information-read-more', href: 'https://docs.woocommerce.com/document/google-listings-and-ads/#contact-information' }`
* @fires gla_documentation_link_click with `{ context: 'settings-no-phone-number-notice', link_id: 'contact-information-read-more', href: 'https://docs.woocommerce.com/document/google-listings-and-ads/#contact-information' }`
* @fires gla_documentation_link_click with `{ context: 'settings-no-store-address-notice', link_id: 'contact-information-read-more', href: 'https://docs.woocommerce.com/document/google-listings-and-ads/#contact-information' }`
*/
const ContactInformation = ( { onPhoneNumberVerified } ) => {
const phone = useGoogleMCPhoneNumber();
-
- /**
- * Since it is still lacking the phone verification state,
- * all onboarding accounts are considered unverified phone numbers.
- *
- * TODO: replace the code at next line back to the original logic with
- * `const initEditing = null;`
- * after the phone verification state can be detected.
- */
- const initEditing = true;
-
const title = mcTitle;
const trackContext = 'setup-mc-contact-information';
@@ -127,7 +116,7 @@ const ContactInformation = ( { onPhoneNumberVerified } ) => {
diff --git a/js/src/components/contact-information/phone-number-card/phone-number-card-preview.js b/js/src/components/contact-information/phone-number-card/phone-number-card-preview.js
index f81a92de23..6b503780a0 100644
--- a/js/src/components/contact-information/phone-number-card/phone-number-card-preview.js
+++ b/js/src/components/contact-information/phone-number-card/phone-number-card-preview.js
@@ -9,6 +9,7 @@ import { __ } from '@wordpress/i18n';
import { APPEARANCE } from '.~/components/account-card';
import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber';
import ContactInformationPreviewCard from '../contact-information-preview-card';
+import './phone-number-card-preview.scss';
/**
* Triggered when phone number edit button is clicked.
@@ -35,7 +36,21 @@ export function PhoneNumberCardPreview( { editHref, learnMore } ) {
if ( loaded ) {
if ( data.isValid ) {
- content = data.display;
+ const verificationStatus = data.isVerified ? (
+
+ { __( 'Verified', 'google-listings-and-ads' ) }
+
+ ) : (
+
+ { __( 'Unverified', 'google-listings-and-ads' ) }
+
+ );
+ content = (
+ <>
+ { data.display }
+ { verificationStatus }
+ >
+ );
} else {
warning = __(
'Please add your phone number',
diff --git a/js/src/components/contact-information/phone-number-card/phone-number-card-preview.scss b/js/src/components/contact-information/phone-number-card/phone-number-card-preview.scss
new file mode 100644
index 0000000000..dd46bc1516
--- /dev/null
+++ b/js/src/components/contact-information/phone-number-card/phone-number-card-preview.scss
@@ -0,0 +1,15 @@
+.gla-contact-info-preview-card {
+ .gla-account-card__description {
+ gap: $grid-unit-05;
+ }
+}
+
+.gla-phone-number-card-preview {
+ &__verified-status {
+ color: $alert-green;
+ }
+
+ &__unverified-status {
+ color: $alert-red;
+ }
+}
diff --git a/js/src/components/contact-information/phone-number-card/phone-number-card.js b/js/src/components/contact-information/phone-number-card/phone-number-card.js
index 4892ad8dd4..8068784af8 100644
--- a/js/src/components/contact-information/phone-number-card/phone-number-card.js
+++ b/js/src/components/contact-information/phone-number-card/phone-number-card.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
-import { useState, useEffect } from '@wordpress/element';
+import { useState, useEffect, useRef } from '@wordpress/element';
import { CardDivider } from '@wordpress/components';
import { Spinner } from '@woocommerce/components';
@@ -101,10 +101,10 @@ function EditPhoneNumberCard( { phoneNumber, onPhoneNumberVerified } ) {
* @param {boolean|null} [props.initEditing=null] Specify the inital UI state.
* `true`: initialize with the editing UI.
* `false`: initialize with the viewing UI.
- * `null`: determine the initial UI state according to the `data.isValid` after the `phoneNumber` loaded.
+ * `null`: determine the initial UI state according to the `data.isVerified` after the `phoneNumber` loaded.
* @param {Function} [props.onEditClick] Called when clicking on "Edit" button.
* If this callback is omitted, it will enter edit mode when clicking on "Edit" button.
- * @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified in edit mode.
+ * @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified or has been verified.
*
* @fires gla_mc_phone_number_edit_button_click
*/
@@ -117,14 +117,27 @@ const PhoneNumberCard = ( {
} ) => {
const { loaded, data } = phoneNumber;
const [ isEditing, setEditing ] = useState( initEditing );
+ const onPhoneNumberVerifiedRef = useRef();
+ onPhoneNumberVerifiedRef.current = onPhoneNumberVerified;
+
+ const { isVerified } = data;
// Handle the initial UI state of `initEditing = null`.
// The `isEditing` state is on hold. Determine it after the `phoneNumber` loaded.
useEffect( () => {
if ( loaded && isEditing === null ) {
- setEditing( ! data.isValid );
+ setEditing( ! isVerified );
+ }
+ }, [ loaded, isVerified, isEditing ] );
+
+ // If `initEditing` is true, EditPhoneNumberCard takes care of the call of `onPhoneNumberVerified`
+ // after the phone number is verified. If `initEditing` is false or null, this useEffect handles
+ // the call of `onPhoneNumberVerified` when the loaded phone number has already been verified.
+ useEffect( () => {
+ if ( loaded && initEditing !== true && isVerified ) {
+ onPhoneNumberVerifiedRef.current();
}
- }, [ loaded, data.isValid, isEditing ] );
+ }, [ loaded, isVerified, initEditing ] );
// Return a simple loading AccountCard since the initial edit state is unknown before loaded.
if ( isEditing === null ) {
diff --git a/js/src/data/selectors.js b/js/src/data/selectors.js
index 92ff118343..c8648b04a1 100644
--- a/js/src/data/selectors.js
+++ b/js/src/data/selectors.js
@@ -10,6 +10,10 @@ import createSelector from 'rememo';
import { STORE_KEY } from './constants';
import { getReportQuery, getReportKey, getPerformanceQuery } from './utils';
+/**
+ * @typedef {import('.~/data/actions').CountryCode} CountryCode
+ */
+
export const getShippingRates = ( state ) => {
return state.mc.shipping.rates;
};
@@ -74,11 +78,42 @@ export const getExistingGoogleAdsAccounts = ( state ) => {
return state.mc.accounts.existing_ads;
};
+/**
+ * @typedef {Object} Address
+ * @property {string|null} street_address Street-level part of the address. `null` when empty.
+ * @property {string|null} locality City, town or commune. `null` when empty.
+ * @property {string|null} region Top-level administrative subdivision of the country. `null` when empty.
+ * @property {string|null} postal_code Postal code or ZIP. `null` when empty.
+ * @property {CountryCode} country Two-letter country code in ISO 3166-1 alpha-2 format. Example: 'US'.
+ *
+ * @typedef {Object} ContactInformation
+ * @property {number} id The Google Merchant Center account ID.
+ * @property {string|null} phone_number The phone number in international format associated with the Google Merchant Center account. Example: '+12133734253'. `null` if the phone number is not yet set.
+ * @property {'verified'|'unverified'|null} phone_verification_status The verification status of the phone number associated with the Google Merchant Center account. `null` if the phone number is not yet set.
+ * @property {Address|null} mc_address The address associated with the Google Merchant Center account. `null` if the address is not yet set.
+ * @property {Address|null} wc_address The WooCommerce store address. `null` if the address is not yet set.
+ * @property {boolean} is_mc_address_different Whether the Google Merchant Center account address is different than the WooCommerce store address.
+ * @property {string[]} wc_address_errors The errors associated with the WooCommerce store address.
+ */
+
+/**
+ * Select the state of contact information associated with the Google Merchant Center account.
+ *
+ * @param {Object} state The current store state will be injected by `wp.data`.
+ * @return {ContactInformation|null} The contact information associated with the Google Merchant Center account. It would return `null` before the data is fetched.
+ */
export const getGoogleMCContactInformation = ( state ) => {
return state.mc.contact;
};
-// Create another selector to separate the `hasFinishedResolution` state with `getGoogleMCContactInformation`.
+/**
+ * Select the state of phone number associated with the Google Merchant Center account.
+ *
+ * Create another selector to separate the `hasFinishedResolution` state with `getGoogleMCContactInformation`.
+ *
+ * @param {Object} state The current store state will be injected by `wp.data`.
+ * @return {{ data: ContactInformation|null, loaded: boolean }} The payload of contact information associated with the Google Merchant Center account and its loaded state.
+ */
export const getGoogleMCPhoneNumber = createRegistrySelector(
( select ) => ( state ) => {
const selector = select( STORE_KEY );
diff --git a/js/src/hooks/useGoogleMCPhoneNumber.js b/js/src/hooks/useGoogleMCPhoneNumber.js
index 59072ec38e..1be1a8ce26 100644
--- a/js/src/hooks/useGoogleMCPhoneNumber.js
+++ b/js/src/hooks/useGoogleMCPhoneNumber.js
@@ -14,6 +14,7 @@ const emptyData = {
countryCallingCode: '',
nationalNumber: '',
isValid: false,
+ isVerified: false,
display: '',
};
@@ -29,13 +30,14 @@ const emptyData = {
* @property {string} countryCallingCode The country calling code. Example: '1'.
* @property {string} nationalNumber The national (significant) number. Example: '2133734253'.
* @property {boolean} isValid Whether the phone number is valid.
+ * @property {boolean} isVerified Whether the phone number is verified.
* @property {string} display The phone number string in international format. Example: '+1 213 373 4253'.
*/
/**
* A hook to load user's phone number data from Google Merchant Center.
*
- * @return {PhoneNumber} [description]
+ * @return {PhoneNumber} The payload of parsed phone number associated with the Google Merchant Center account and its loaded state.
*/
export default function useGoogleMCPhoneNumber() {
return useSelect( ( select ) => {
@@ -50,6 +52,8 @@ export default function useGoogleMCPhoneNumber() {
data = {
...parsed,
isValid: parsed.isValid(),
+ isVerified:
+ contact.phone_verification_status === 'verified',
display: parsed.formatInternational(),
};
delete data.metadata;
diff --git a/js/src/setup-mc/setup-stepper/index.js b/js/src/setup-mc/setup-stepper/index.js
index d5941f41b1..7a45bcbc08 100644
--- a/js/src/setup-mc/setup-stepper/index.js
+++ b/js/src/setup-mc/setup-stepper/index.js
@@ -13,11 +13,7 @@ import useMCSetup from '.~/hooks/useMCSetup';
import stepNameKeyMap from './stepNameKeyMap';
const SetupStepper = () => {
- const {
- hasFinishedResolution,
- data: mcSetup,
- invalidateResolution: mcSetupInvalidateResolution,
- } = useMCSetup();
+ const { hasFinishedResolution, data: mcSetup } = useMCSetup();
if ( ! hasFinishedResolution && ! mcSetup ) {
return ;
@@ -36,16 +32,7 @@ const SetupStepper = () => {
return null;
}
- const handleRefetchSavedStep = () => {
- mcSetupInvalidateResolution();
- };
-
- return (
-
- );
+ return ;
};
export default SetupStepper;
diff --git a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js
index eca206baf3..b9677f2800 100644
--- a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js
+++ b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js
@@ -28,10 +28,9 @@ import stepNameKeyMap from './stepNameKeyMap';
/**
* @param {Object} props React props
* @param {string} [props.savedStep] A saved step overriding the current step
- * @param {Function} [props.onRefetchSavedStep] Callback when Saved Step is updated
* @fires gla_setup_mc with `{ target: 'step1_continue' | 'step2_continue' | 'step3_continue', trigger: 'click' }`.
*/
-const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
+const SavedSetupStepper = ( { savedStep } ) => {
const [ step, setStep ] = useState( savedStep );
const { settings } = useSettings();
@@ -83,7 +82,6 @@ const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
trigger: 'click',
} );
setStep( stepNameKeyMap.product_listings );
- onRefetchSavedStep();
};
const handleSetupListingsContinue = () => {
@@ -92,7 +90,6 @@ const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
trigger: 'click',
} );
setStep( stepNameKeyMap.store_requirements );
- onRefetchSavedStep();
};
const handleStoreRequirementsContinue = () => {
@@ -101,11 +98,11 @@ const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
trigger: 'click',
} );
setStep( stepNameKeyMap.paid_ads );
- onRefetchSavedStep();
};
const handleStepClick = ( stepKey ) => {
- if ( Number( stepKey ) <= Number( savedStep ) ) {
+ // Only allow going back to the previous steps.
+ if ( Number( stepKey ) < Number( step ) ) {
setStep( stepKey );
}
};
diff --git a/src/API/Site/Controllers/MerchantCenter/ContactInformationController.php b/src/API/Site/Controllers/MerchantCenter/ContactInformationController.php
index a59a79d580..6481714d93 100644
--- a/src/API/Site/Controllers/MerchantCenter/ContactInformationController.php
+++ b/src/API/Site/Controllers/MerchantCenter/ContactInformationController.php
@@ -124,35 +124,40 @@ protected function get_contact_information_endpoint_edit_callback(): callable {
*/
protected function get_schema_properties(): array {
return [
- 'id' => [
+ 'id' => [
'type' => 'integer',
'description' => __( 'The Merchant Center account ID.', 'google-listings-and-ads' ),
'context' => [ 'view', 'edit' ],
'validate_callback' => 'rest_validate_request_arg',
],
- 'phone_number' => [
+ 'phone_number' => [
'type' => 'string',
'description' => __( 'The phone number associated with the Merchant Center account.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
],
- 'mc_address' => [
+ 'phone_verification_status' => [
+ 'type' => 'string',
+ 'description' => __( 'The verification status of the phone number associated with the Merchant Center account.', 'google-listings-and-ads' ),
+ 'context' => [ 'view' ],
+ ],
+ 'mc_address' => [
'type' => 'object',
'description' => __( 'The address associated with the Merchant Center account.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
'properties' => $this->get_address_schema(),
],
- 'wc_address' => [
+ 'wc_address' => [
'type' => 'object',
'description' => __( 'The WooCommerce store address.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
'properties' => $this->get_address_schema(),
],
- 'is_mc_address_different' => [
+ 'is_mc_address_different' => [
'type' => 'boolean',
'description' => __( 'Whether the Merchant Center account address is different than the WooCommerce store address.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
],
- 'wc_address_errors' => [
+ 'wc_address_errors' => [
'type' => 'array',
'description' => __( 'The errors associated with the WooCommerce address', 'google-listings-and-ads' ),
'context' => [ 'view' ],
@@ -215,10 +220,11 @@ public function get_update_args(): array {
* @return Response
*/
protected function get_contact_information_response( ?AccountBusinessInformation $contact_information, Request $request ): Response {
- $phone_number = null;
- $mc_address = null;
- $wc_address = null;
- $is_address_diff = false;
+ $phone_number = null;
+ $phone_verification_status = null;
+ $mc_address = null;
+ $wc_address = null;
+ $is_address_diff = false;
if ( $this->settings->get_store_address() instanceof AccountAddress ) {
$wc_address = $this->settings->get_store_address();
@@ -228,7 +234,8 @@ protected function get_contact_information_response( ?AccountBusinessInformation
if ( $contact_information instanceof AccountBusinessInformation ) {
if ( ! empty( $contact_information->getPhoneNumber() ) ) {
try {
- $phone_number = PhoneNumber::cast( $contact_information->getPhoneNumber() )->get();
+ $phone_number = PhoneNumber::cast( $contact_information->getPhoneNumber() )->get();
+ $phone_verification_status = strtolower( $contact_information->getPhoneVerificationStatus() );
} catch ( InvalidValue $exception ) {
// log and fail silently
do_action( 'woocommerce_gla_exception', $exception, __METHOD__ );
@@ -249,12 +256,13 @@ protected function get_contact_information_response( ?AccountBusinessInformation
return $this->prepare_item_for_response(
[
- 'id' => $this->options->get_merchant_id(),
- 'phone_number' => $phone_number,
- 'mc_address' => self::serialize_address( $mc_address ),
- 'wc_address' => self::serialize_address( $wc_address ),
- 'is_mc_address_different' => $is_address_diff,
- 'wc_address_errors' => $wc_address_errors,
+ 'id' => $this->options->get_merchant_id(),
+ 'phone_number' => $phone_number,
+ 'phone_verification_status' => $phone_verification_status,
+ 'mc_address' => self::serialize_address( $mc_address ),
+ 'wc_address' => self::serialize_address( $wc_address ),
+ 'is_mc_address_different' => $is_address_diff,
+ 'wc_address_errors' => $wc_address_errors,
],
$request
);
diff --git a/src/MerchantCenter/MerchantCenterService.php b/src/MerchantCenter/MerchantCenterService.php
index c4017fd1b3..6b957e2376 100644
--- a/src/MerchantCenter/MerchantCenterService.php
+++ b/src/MerchantCenter/MerchantCenterService.php
@@ -293,8 +293,9 @@ protected function maybe_add_contact_info_issue( array $issues, DateTime $cache_
*/
protected function is_mc_contact_information_setup(): bool {
$is_setup = [
- 'phone_number' => false,
- 'address' => false,
+ 'phone_number' => false,
+ 'phone_verification_status' => false,
+ 'address' => false,
];
try {
@@ -310,7 +311,8 @@ protected function is_mc_contact_information_setup(): bool {
}
if ( $contact_info instanceof AccountBusinessInformation ) {
- $is_setup['phone_number'] = ! empty( $contact_info->getPhoneNumber() );
+ $is_setup['phone_number'] = ! empty( $contact_info->getPhoneNumber() );
+ $is_setup['phone_verification_status'] = 'VERIFIED' === $contact_info->getPhoneVerificationStatus();
/** @var Settings $settings */
$settings = $this->container->get( Settings::class );
@@ -323,7 +325,7 @@ protected function is_mc_contact_information_setup(): bool {
}
}
- return $is_setup['phone_number'] && $is_setup['address'];
+ return $is_setup['phone_number'] && $is_setup['phone_verification_status'] && $is_setup['address'];
}
/**
diff --git a/tests/Tools/HelperTrait/MerchantTrait.php b/tests/Tools/HelperTrait/MerchantTrait.php
index 00fe186b78..a2d0055819 100644
--- a/tests/Tools/HelperTrait/MerchantTrait.php
+++ b/tests/Tools/HelperTrait/MerchantTrait.php
@@ -48,6 +48,7 @@ public function get_valid_account(): Account {
public function get_valid_business_info(): AccountBusinessInformation {
$business_info = new AccountBusinessInformation();
$business_info->setPhoneNumber( $this->valid_account_phone_number );
+ $business_info->setPhoneVerificationStatus( 'VERIFIED' );
$business_info->setAddress( $this->get_sample_address() );
return $business_info;