mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-01 07:02:48 +08:00
Merge pull request #3097 from woocommerce/PCP-4183-manual-connection-credentials-are-not-working
Manual Connection credentials are not working (4183)
This commit is contained in:
commit
8258c1c993
8 changed files with 87 additions and 46 deletions
|
@ -18,6 +18,7 @@ const DataStoreControl = React.forwardRef(
|
||||||
control: ControlComponent,
|
control: ControlComponent,
|
||||||
value: externalValue,
|
value: externalValue,
|
||||||
onChange,
|
onChange,
|
||||||
|
onConfirm = null,
|
||||||
delay = 300,
|
delay = 300,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
|
@ -25,7 +26,9 @@ const DataStoreControl = React.forwardRef(
|
||||||
) => {
|
) => {
|
||||||
const [ internalValue, setInternalValue ] = useState( externalValue );
|
const [ internalValue, setInternalValue ] = useState( externalValue );
|
||||||
const onChangeRef = useRef( onChange );
|
const onChangeRef = useRef( onChange );
|
||||||
|
const onConfirmRef = useRef( onConfirm );
|
||||||
onChangeRef.current = onChange;
|
onChangeRef.current = onChange;
|
||||||
|
onConfirmRef.current = onConfirm;
|
||||||
|
|
||||||
const debouncedUpdate = useRef(
|
const debouncedUpdate = useRef(
|
||||||
debounce( ( value ) => {
|
debounce( ( value ) => {
|
||||||
|
@ -36,7 +39,7 @@ const DataStoreControl = React.forwardRef(
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
setInternalValue( externalValue );
|
setInternalValue( externalValue );
|
||||||
debouncedUpdate?.cancel();
|
debouncedUpdate?.cancel();
|
||||||
}, [ externalValue ] );
|
}, [ debouncedUpdate, externalValue ] );
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
return () => debouncedUpdate?.cancel();
|
return () => debouncedUpdate?.cancel();
|
||||||
|
@ -50,12 +53,25 @@ const DataStoreControl = React.forwardRef(
|
||||||
[ debouncedUpdate ]
|
[ debouncedUpdate ]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback(
|
||||||
|
( event ) => {
|
||||||
|
if ( onConfirmRef.current && event.key === 'Enter' ) {
|
||||||
|
event.preventDefault();
|
||||||
|
debouncedUpdate.flush();
|
||||||
|
onConfirmRef.current();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ debouncedUpdate ]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlComponent
|
<ControlComponent
|
||||||
ref={ ref }
|
ref={ ref }
|
||||||
{ ...props }
|
{ ...props }
|
||||||
value={ internalValue }
|
value={ internalValue }
|
||||||
onChange={ handleChange }
|
onChange={ handleChange }
|
||||||
|
onKeyDown={ handleKeyDown }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,7 @@ const ManualConnectionForm = () => {
|
||||||
label={ clientIdLabel }
|
label={ clientIdLabel }
|
||||||
value={ manualClientId }
|
value={ manualClientId }
|
||||||
onChange={ setManualClientId }
|
onChange={ setManualClientId }
|
||||||
|
onConfirm={ handleManualConnect }
|
||||||
className={ classNames( {
|
className={ classNames( {
|
||||||
'ppcp--has-error': ! clientValid,
|
'ppcp--has-error': ! clientValid,
|
||||||
} ) }
|
} ) }
|
||||||
|
@ -173,6 +174,7 @@ const ManualConnectionForm = () => {
|
||||||
label={ secretKeyLabel }
|
label={ secretKeyLabel }
|
||||||
value={ manualClientSecret }
|
value={ manualClientSecret }
|
||||||
onChange={ setManualClientSecret }
|
onChange={ setManualClientSecret }
|
||||||
|
onConfirm={ handleManualConnect }
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default {
|
||||||
SET_PERSISTENT: 'ppcp/common/SET_PERSISTENT',
|
SET_PERSISTENT: 'ppcp/common/SET_PERSISTENT',
|
||||||
RESET: 'ppcp/common/RESET',
|
RESET: 'ppcp/common/RESET',
|
||||||
HYDRATE: 'ppcp/common/HYDRATE',
|
HYDRATE: 'ppcp/common/HYDRATE',
|
||||||
|
SET_MERCHANT: 'ppcp/common/SET_MERCHANT',
|
||||||
RESET_MERCHANT: 'ppcp/common/RESET_MERCHANT',
|
RESET_MERCHANT: 'ppcp/common/RESET_MERCHANT',
|
||||||
|
|
||||||
// Activity management (advanced solution that replaces the isBusy state).
|
// Activity management (advanced solution that replaces the isBusy state).
|
||||||
|
|
|
@ -110,6 +110,17 @@ export const setManualConnectionMode = ( useManualConnection ) =>
|
||||||
export const setWebhooks = ( webhooks ) =>
|
export const setWebhooks = ( webhooks ) =>
|
||||||
setPersistent( 'webhooks', 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.
|
* Reset merchant details in the store.
|
||||||
*
|
*
|
||||||
|
|
|
@ -141,18 +141,27 @@ export const useWebhooks = () => {
|
||||||
export const useMerchantInfo = () => {
|
export const useMerchantInfo = () => {
|
||||||
const { isReady, features } = useHooks();
|
const { isReady, features } = useHooks();
|
||||||
const merchant = useMerchant();
|
const merchant = useMerchant();
|
||||||
const { refreshMerchantData } = useDispatch( STORE_NAME );
|
const { refreshMerchantData, setMerchant } = useDispatch( STORE_NAME );
|
||||||
|
|
||||||
const verifyLoginStatus = useCallback( async () => {
|
const verifyLoginStatus = useCallback( async () => {
|
||||||
const result = await refreshMerchantData();
|
const result = await refreshMerchantData();
|
||||||
|
|
||||||
if ( ! result.success ) {
|
if ( ! result.success || ! result.merchant ) {
|
||||||
throw new Error( result?.message || result?.error?.message );
|
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.
|
// Verify if the server state is "connected" and we have a merchant ID.
|
||||||
return merchant?.isConnected && merchant?.id;
|
if ( newMerchant?.isConnected && newMerchant?.id ) {
|
||||||
}, [ refreshMerchantData, merchant ] );
|
// Update the verified merchant details in Redux.
|
||||||
|
setMerchant( newMerchant );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}, [ refreshMerchantData, setMerchant ] );
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isReady,
|
isReady,
|
||||||
|
@ -225,6 +234,8 @@ export const useBusyState = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
startActivity,
|
||||||
|
stopActivity,
|
||||||
withActivity, // HOC
|
withActivity, // HOC
|
||||||
isBusy, // Boolean.
|
isBusy, // Boolean.
|
||||||
};
|
};
|
||||||
|
|
|
@ -114,6 +114,10 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, {
|
||||||
features: Object.freeze( { ...defaultTransient.features } ),
|
features: Object.freeze( { ...defaultTransient.features } ),
|
||||||
} ),
|
} ),
|
||||||
|
|
||||||
|
[ ACTION_TYPES.SET_MERCHANT ]: ( state, payload ) => {
|
||||||
|
return changePersistent( state, { merchant: payload.merchant } );
|
||||||
|
},
|
||||||
|
|
||||||
[ ACTION_TYPES.HYDRATE ]: ( state, payload ) => {
|
[ ACTION_TYPES.HYDRATE ]: ( state, payload ) => {
|
||||||
const newState = changePersistent( state, payload.data );
|
const newState = changePersistent( state, payload.data );
|
||||||
|
|
||||||
|
|
|
@ -10,19 +10,7 @@ const PAYPAL_PARTNER_SDK_URL =
|
||||||
|
|
||||||
const MESSAGES = {
|
const MESSAGES = {
|
||||||
CONNECTED: __( 'Connected to PayPal', 'woocommerce-paypal-payments' ),
|
CONNECTED: __( 'Connected to PayPal', 'woocommerce-paypal-payments' ),
|
||||||
POPUP_BLOCKED: __(
|
API_ERROR: __(
|
||||||
'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: __(
|
|
||||||
'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.',
|
'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
|
@ -33,17 +21,16 @@ const MESSAGES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ACTIVITIES = {
|
const ACTIVITIES = {
|
||||||
CONNECT_SANDBOX: 'ISU_LOGIN_SANDBOX',
|
OAUTH_VERIFY: 'oauth/login',
|
||||||
CONNECT_PRODUCTION: 'ISU_LOGIN_PRODUCTION',
|
API_LOGIN: 'auth/api-login',
|
||||||
CONNECT_ISU: 'ISU_LOGIN',
|
API_VERIFY: 'auth/verify-login',
|
||||||
CONNECT_MANUAL: 'MANUAL_LOGIN',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useHandleOnboardingButton = ( isSandbox ) => {
|
export const useHandleOnboardingButton = ( isSandbox ) => {
|
||||||
const { sandboxOnboardingUrl } = CommonHooks.useSandbox();
|
const { sandboxOnboardingUrl } = CommonHooks.useSandbox();
|
||||||
const { productionOnboardingUrl } = CommonHooks.useProduction();
|
const { productionOnboardingUrl } = CommonHooks.useProduction();
|
||||||
const products = OnboardingHooks.useDetermineProducts();
|
const products = OnboardingHooks.useDetermineProducts();
|
||||||
const { withActivity } = CommonHooks.useBusyState();
|
const { withActivity, startActivity } = CommonHooks.useBusyState();
|
||||||
const { authenticateWithOAuth } = CommonHooks.useAuthentication();
|
const { authenticateWithOAuth } = CommonHooks.useAuthentication();
|
||||||
const [ onboardingUrl, setOnboardingUrl ] = useState( '' );
|
const [ onboardingUrl, setOnboardingUrl ] = useState( '' );
|
||||||
const [ scriptLoaded, setScriptLoaded ] = useState( false );
|
const [ scriptLoaded, setScriptLoaded ] = useState( false );
|
||||||
|
@ -123,16 +110,15 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
|
||||||
* frame before the REST endpoint returns a value. Using "withActivity" is more of a
|
* 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.
|
* visual cue to the user that something is still processing in the background.
|
||||||
*/
|
*/
|
||||||
await withActivity(
|
startActivity(
|
||||||
ACTIVITIES.CONNECT_ISU,
|
ACTIVITIES.OAUTH_VERIFY,
|
||||||
'Validating the connection details',
|
'Validating the connection details'
|
||||||
async () => {
|
);
|
||||||
await authenticateWithOAuth(
|
|
||||||
sharedId,
|
await authenticateWithOAuth(
|
||||||
authCode,
|
sharedId,
|
||||||
'sandbox' === environment
|
authCode,
|
||||||
);
|
'sandbox' === environment
|
||||||
}
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,11 +154,13 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Base connection is only used for API login (manual connection).
|
||||||
const useConnectionBase = () => {
|
const useConnectionBase = () => {
|
||||||
const { setCompleted } = OnboardingHooks.useSteps();
|
const { setCompleted } = OnboardingHooks.useSteps();
|
||||||
const { createSuccessNotice, createErrorNotice } =
|
const { createSuccessNotice, createErrorNotice } =
|
||||||
useDispatch( noticesStore );
|
useDispatch( noticesStore );
|
||||||
const { verifyLoginStatus } = CommonHooks.useMerchantInfo();
|
const { verifyLoginStatus } = CommonHooks.useMerchantInfo();
|
||||||
|
const { withActivity } = CommonHooks.useBusyState();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleFailed: ( res, genericMessage ) => {
|
handleFailed: ( res, genericMessage ) => {
|
||||||
|
@ -180,18 +168,26 @@ const useConnectionBase = () => {
|
||||||
createErrorNotice( res?.message ?? genericMessage );
|
createErrorNotice( res?.message ?? genericMessage );
|
||||||
},
|
},
|
||||||
handleCompleted: async () => {
|
handleCompleted: async () => {
|
||||||
try {
|
await withActivity(
|
||||||
const loginSuccessful = await verifyLoginStatus();
|
ACTIVITIES.API_VERIFY,
|
||||||
|
'Verifying Authentication',
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const loginSuccessful = await verifyLoginStatus();
|
||||||
|
|
||||||
if ( loginSuccessful ) {
|
if ( loginSuccessful ) {
|
||||||
createSuccessNotice( MESSAGES.CONNECTED );
|
createSuccessNotice( MESSAGES.CONNECTED );
|
||||||
await setCompleted( true );
|
await setCompleted( true );
|
||||||
} else {
|
} else {
|
||||||
createErrorNotice( MESSAGES.LOGIN_FAILED );
|
createErrorNotice( MESSAGES.LOGIN_FAILED );
|
||||||
|
}
|
||||||
|
} catch ( error ) {
|
||||||
|
createErrorNotice(
|
||||||
|
error.message ?? MESSAGES.LOGIN_FAILED
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch ( error ) {
|
);
|
||||||
createErrorNotice( error.message ?? MESSAGES.LOGIN_FAILED );
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
createErrorNotice,
|
createErrorNotice,
|
||||||
};
|
};
|
||||||
|
@ -218,7 +214,7 @@ export const useDirectAuthentication = () => {
|
||||||
|
|
||||||
const handleDirectAuthentication = async ( connectionDetails ) => {
|
const handleDirectAuthentication = async ( connectionDetails ) => {
|
||||||
return withActivity(
|
return withActivity(
|
||||||
ACTIVITIES.CONNECT_MANUAL,
|
ACTIVITIES.API_LOGIN,
|
||||||
'Connecting manually via Client ID and Secret',
|
'Connecting manually via Client ID and Secret',
|
||||||
async () => {
|
async () => {
|
||||||
let data;
|
let data;
|
||||||
|
@ -250,7 +246,7 @@ export const useDirectAuthentication = () => {
|
||||||
if ( res.success ) {
|
if ( res.success ) {
|
||||||
await handleCompleted();
|
await handleCompleted();
|
||||||
} else {
|
} else {
|
||||||
handleFailed( res, MESSAGES.MANUAL_ERROR );
|
handleFailed( res, MESSAGES.API_ERROR );
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.success;
|
return res.success;
|
||||||
|
|
|
@ -175,7 +175,7 @@ class AuthenticationRestEndpoint extends RestEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
$account = $this->authentication_manager->get_account_details();
|
$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 );
|
return $this->return_success( $response );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue