Skip to content

Commit

Permalink
Merge pull request #3097 from woocommerce/PCP-4183-manual-connection-…
Browse files Browse the repository at this point in the history
…credentials-are-not-working

Manual Connection credentials are not working (4183)
  • Loading branch information
Dinamiko authored Feb 12, 2025
2 parents 8b9b664 + a752048 commit 8258c1c
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ const DataStoreControl = React.forwardRef(
control: ControlComponent,
value: externalValue,
onChange,
onConfirm = null,
delay = 300,
...props
},
ref
) => {
const [ internalValue, setInternalValue ] = useState( externalValue );
const onChangeRef = useRef( onChange );
const onConfirmRef = useRef( onConfirm );
onChangeRef.current = onChange;
onConfirmRef.current = onConfirm;

const debouncedUpdate = useRef(
debounce( ( value ) => {
Expand All @@ -36,7 +39,7 @@ const DataStoreControl = React.forwardRef(
useEffect( () => {
setInternalValue( externalValue );
debouncedUpdate?.cancel();
}, [ externalValue ] );
}, [ debouncedUpdate, externalValue ] );

useEffect( () => {
return () => debouncedUpdate?.cancel();
Expand All @@ -50,12 +53,25 @@ const DataStoreControl = React.forwardRef(
[ debouncedUpdate ]
);

const handleKeyDown = useCallback(
( event ) => {
if ( onConfirmRef.current && event.key === 'Enter' ) {
event.preventDefault();
debouncedUpdate.flush();
onConfirmRef.current();
return false;
}
},
[ debouncedUpdate ]
);

return (
<ControlComponent
ref={ ref }
{ ...props }
value={ internalValue }
onChange={ handleChange }
onKeyDown={ handleKeyDown }
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ const ManualConnectionForm = () => {
label={ clientIdLabel }
value={ manualClientId }
onChange={ setManualClientId }
onConfirm={ handleManualConnect }
className={ classNames( {
'ppcp--has-error': ! clientValid,
} ) }
Expand All @@ -173,6 +174,7 @@ const ManualConnectionForm = () => {
label={ secretKeyLabel }
value={ manualClientSecret }
onChange={ setManualClientSecret }
onConfirm={ handleManualConnect }
type="password"
/>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default {
SET_PERSISTENT: 'ppcp/common/SET_PERSISTENT',
RESET: 'ppcp/common/RESET',
HYDRATE: 'ppcp/common/HYDRATE',
SET_MERCHANT: 'ppcp/common/SET_MERCHANT',
RESET_MERCHANT: 'ppcp/common/RESET_MERCHANT',

// Activity management (advanced solution that replaces the isBusy state).
Expand Down
11 changes: 11 additions & 0 deletions modules/ppcp-settings/resources/js/data/common/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ export const setManualConnectionMode = ( useManualConnection ) =>
export const setWebhooks = ( webhooks ) =>
setPersistent( 'webhooks', webhooks );

/**
* Replace merchant details in the store.
*
* @param {Object} merchant - The new merchant details.
* @return {Action} The action.
*/
export const setMerchant = ( merchant ) => ( {
type: ACTION_TYPES.SET_MERCHANT,
payload: { merchant },
} );

/**
* Reset merchant details in the store.
*
Expand Down
19 changes: 15 additions & 4 deletions modules/ppcp-settings/resources/js/data/common/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,27 @@ export const useWebhooks = () => {
export const useMerchantInfo = () => {
const { isReady, features } = useHooks();
const merchant = useMerchant();
const { refreshMerchantData } = useDispatch( STORE_NAME );
const { refreshMerchantData, setMerchant } = useDispatch( STORE_NAME );

const verifyLoginStatus = useCallback( async () => {
const result = await refreshMerchantData();

if ( ! result.success ) {
if ( ! result.success || ! result.merchant ) {
throw new Error( result?.message || result?.error?.message );
}

const newMerchant = result.merchant;

// Verify if the server state is "connected" and we have a merchant ID.
return merchant?.isConnected && merchant?.id;
}, [ refreshMerchantData, merchant ] );
if ( newMerchant?.isConnected && newMerchant?.id ) {
// Update the verified merchant details in Redux.
setMerchant( newMerchant );

return true;
}

return false;
}, [ refreshMerchantData, setMerchant ] );

return {
isReady,
Expand Down Expand Up @@ -225,6 +234,8 @@ export const useBusyState = () => {
);

return {
startActivity,
stopActivity,
withActivity, // HOC
isBusy, // Boolean.
};
Expand Down
4 changes: 4 additions & 0 deletions modules/ppcp-settings/resources/js/data/common/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, {
features: Object.freeze( { ...defaultTransient.features } ),
} ),

[ ACTION_TYPES.SET_MERCHANT ]: ( state, payload ) => {
return changePersistent( state, { merchant: payload.merchant } );
},

[ ACTION_TYPES.HYDRATE ]: ( state, payload ) => {
const newState = changePersistent( state, payload.data );

Expand Down
78 changes: 37 additions & 41 deletions modules/ppcp-settings/resources/js/hooks/useHandleConnections.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,7 @@ const PAYPAL_PARTNER_SDK_URL =

const MESSAGES = {
CONNECTED: __( 'Connected to PayPal', 'woocommerce-paypal-payments' ),
POPUP_BLOCKED: __(
'Popup blocked. Please allow popups for this site to connect to PayPal.',
'woocommerce-paypal-payments'
),
SANDBOX_ERROR: __(
'Could not generate a Sandbox login link.',
'woocommerce-paypal-payments'
),
PRODUCTION_ERROR: __(
'Could not generate a login link.',
'woocommerce-paypal-payments'
),
MANUAL_ERROR: __(
API_ERROR: __(
'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.',
'woocommerce-paypal-payments'
),
Expand All @@ -33,17 +21,16 @@ const MESSAGES = {
};

const ACTIVITIES = {
CONNECT_SANDBOX: 'ISU_LOGIN_SANDBOX',
CONNECT_PRODUCTION: 'ISU_LOGIN_PRODUCTION',
CONNECT_ISU: 'ISU_LOGIN',
CONNECT_MANUAL: 'MANUAL_LOGIN',
OAUTH_VERIFY: 'oauth/login',
API_LOGIN: 'auth/api-login',
API_VERIFY: 'auth/verify-login',
};

export const useHandleOnboardingButton = ( isSandbox ) => {
const { sandboxOnboardingUrl } = CommonHooks.useSandbox();
const { productionOnboardingUrl } = CommonHooks.useProduction();
const products = OnboardingHooks.useDetermineProducts();
const { withActivity } = CommonHooks.useBusyState();
const { withActivity, startActivity } = CommonHooks.useBusyState();
const { authenticateWithOAuth } = CommonHooks.useAuthentication();
const [ onboardingUrl, setOnboardingUrl ] = useState( '' );
const [ scriptLoaded, setScriptLoaded ] = useState( false );
Expand Down Expand Up @@ -123,16 +110,15 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
* frame before the REST endpoint returns a value. Using "withActivity" is more of a
* visual cue to the user that something is still processing in the background.
*/
await withActivity(
ACTIVITIES.CONNECT_ISU,
'Validating the connection details',
async () => {
await authenticateWithOAuth(
sharedId,
authCode,
'sandbox' === environment
);
}
startActivity(
ACTIVITIES.OAUTH_VERIFY,
'Validating the connection details'
);

await authenticateWithOAuth(
sharedId,
authCode,
'sandbox' === environment
);
};

Expand Down Expand Up @@ -168,30 +154,40 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
};
};

// Base connection is only used for API login (manual connection).
const useConnectionBase = () => {
const { setCompleted } = OnboardingHooks.useSteps();
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const { verifyLoginStatus } = CommonHooks.useMerchantInfo();
const { withActivity } = CommonHooks.useBusyState();

return {
handleFailed: ( res, genericMessage ) => {
console.error( 'Connection error', res );
createErrorNotice( res?.message ?? genericMessage );
},
handleCompleted: async () => {
try {
const loginSuccessful = await verifyLoginStatus();

if ( loginSuccessful ) {
createSuccessNotice( MESSAGES.CONNECTED );
await setCompleted( true );
} else {
createErrorNotice( MESSAGES.LOGIN_FAILED );
await withActivity(
ACTIVITIES.API_VERIFY,
'Verifying Authentication',
async () => {
try {
const loginSuccessful = await verifyLoginStatus();

if ( loginSuccessful ) {
createSuccessNotice( MESSAGES.CONNECTED );
await setCompleted( true );
} else {
createErrorNotice( MESSAGES.LOGIN_FAILED );
}
} catch ( error ) {
createErrorNotice(
error.message ?? MESSAGES.LOGIN_FAILED
);
}
}
} catch ( error ) {
createErrorNotice( error.message ?? MESSAGES.LOGIN_FAILED );
}
);
},
createErrorNotice,
};
Expand All @@ -218,7 +214,7 @@ export const useDirectAuthentication = () => {

const handleDirectAuthentication = async ( connectionDetails ) => {
return withActivity(
ACTIVITIES.CONNECT_MANUAL,
ACTIVITIES.API_LOGIN,
'Connecting manually via Client ID and Secret',
async () => {
let data;
Expand Down Expand Up @@ -250,7 +246,7 @@ export const useDirectAuthentication = () => {
if ( res.success ) {
await handleCompleted();
} else {
handleFailed( res, MESSAGES.MANUAL_ERROR );
handleFailed( res, MESSAGES.API_ERROR );
}

return res.success;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public function connect_direct( WP_REST_Request $request ) : WP_REST_Response {
}

$account = $this->authentication_manager->get_account_details();
$response = $this->sanitize_for_javascript( $this->response_map, $account );
$response = $this->sanitize_for_javascript( $account, $this->response_map );

return $this->return_success( $response );
}
Expand Down

0 comments on commit 8258c1c

Please sign in to comment.