React应用调用PayPal API获取用户邮箱时持续返回400 Bad Request错误排查求助
React应用调用PayPal API获取用户邮箱时持续返回400 Bad Request错误排查求助
我现在在做一个React应用,想要获取用户的PayPal邮箱地址,但不管怎么调试,请求总是返回Error 400 - Bad Request - Unable to fetch details to serve the request。
我已经反复核对了PayPal后台配置的返回URI,还有Client ID和Secret ID,但问题依然存在,有没有大佬能帮忙看看问题出在哪?
我的后端代码(Node.js + Express + Axios)
export const handlePaypalOAuthCallback = async (req, res) => { try { const { code, state } = req.query; console.log('PayPal callback received:', { code: code ? 'present' : 'missing', state: state ? 'present' : 'missing', fullQuery: req.query }); if (!code) { throw new Error('Authorization code not received'); } if (!state) { throw new Error('User ID not received in state parameter'); } const CLIENT_ID = "xxx"; const CLIENT_SECRET = "xxx"; if (!CLIENT_ID || !CLIENT_SECRET) { console.error('Missing PayPal credentials'); throw new Error('Server configuration error'); } const auth = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'); console.log('Preparing token request:', { url: 'https://api-m.sandbox.paypal.com/v1/oauth2/token', grantType: 'authorization_code', hasCode: !!code, hasAuth: !!auth, redirectUri: '<my return uri>/api/paypal/oauth/callback' }); const params = new URLSearchParams(); params.append('grant_type', 'authorization_code'); params.append('code', code); params.append('redirect_uri', 'http://dev.gotasker.com.au:4000/api/paypal/oauth/callback'); // Add these additional parameters params.append('scope', 'openid email'); params.append('client_id', CLIENT_ID); params.append('client_secret', CLIENT_SECRET); try { // Get access token const tokenResponse = await axios.post( 'https://api-m.sandbox.paypal.com/v1/oauth2/token', params, { headers: { 'Authorization': `Basic ${auth}`, 'Content-Type': 'application/x-www-form-urlencoded', 'PayPal-Request-Id': `${Date.now()}`, 'Accept': 'application/json', 'Accept-Language': 'en_US' } } ); console.log('Token response received:', { hasAccessToken: !!tokenResponse.data.access_token, tokenType: tokenResponse.data.token_type, scope: tokenResponse.data.scope, status: tokenResponse.status, headers: tokenResponse.headers }); const accessToken = tokenResponse.data.access_token; // Log token details for debugging (first few chars only) console.log('Access token details:', { prefix: accessToken.substring(0, 10), length: accessToken.length, tokenType: tokenResponse.data.token_type }); if (!accessToken || typeof accessToken !== 'string') { throw new Error('Invalid access token received from PayPal'); } // Make the userInfo request with proper authorization header const userInfoResponse = await axios.get( 'https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo', // Changed URL { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'Accept': 'application/json', 'PayPal-Request-Id': `${Date.now()}`, }, } ); // Log successful userInfo response console.log('User info response received:', { status: userInfoResponse.status, hasData: !!userInfoResponse.data, dataKeys: Object.keys(userInfoResponse.data) }); const paypalEmail = userInfoResponse.data.email; if (!paypalEmail) { throw new Error('PayPal email not received in user info response'); } // Update user with PayPal email const userId = state; const user = await UserModel.findById(userId); if (!user) { throw new Error('User not found'); } user.paypalEmail = paypalEmail; user.paypalVerified = true; await user.save(); console.log('User PayPal email updated successfully:', { userId: user._id, verified: true }); // Redirect back to frontend with success res.redirect(`${process.env.FRONTEND_URL}/account?paypal=success`); } catch (error) { // Enhanced error logging console.error('API request failed:', { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, message: error.message, headers: error.response?.headers, config: { url: error.config?.url, method: error.config?.method, headers: { ...error.config?.headers, Authorization: error.config?.headers?.Authorization ? `${error.config.headers.Authorization.substring(0, 10)}...` : 'missing' } }, wwwAuthenticate: error.response?.headers?.['www-authenticate'] }); throw error; } } catch (error) { console.error('PayPal OAuth error:', { message: error.message, response: error.response?.data, status: error.response?.status, statusText: error.response?.statusText }); res.redirect(`${process.env.FRONTEND_URL}/account?paypal=error&message=${encodeURIComponent(error.message)}`); } };
我的前端代码(React)
const PayPalLoginCard = () => { const { user, updateUserProfile } = useContext(AuthContext); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [successMessage, setSuccessMessage] = useState(''); const location = useLocation(); const navigate = useNavigate(); useEffect(() => { // Handle OAuth response const searchParams = new URLSearchParams(location.search); const paypalStatus = searchParams.get('paypal'); if (paypalStatus === 'success') { setSuccessMessage('PayPal account connected successfully'); // Remove query params navigate('/account', { replace: true }); } else if (paypalStatus === 'error') { setError(searchParams.get('message') || 'Failed to connect PayPal account'); navigate('/account', { replace: true }); } }, [location, navigate]); const handlePayPalLogin = () => { try { const PAYPAL_CLIENT_ID = "xx"; const REDIRECT_URI = 'http://dev.gotasker.com.au:4000/api/paypal/oauth/callback'; const userId = user?.id; if (!userId) { throw new Error('User ID not available'); } // Updated scopes and parameters const scopes = 'openid email'; const authUrl = `https://www.sandbox.paypal.com/signin/authorize?client_id=${PAYPAL_CLIENT_ID}&response_type=code&scope=${scopes}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&state=${userId}&nonce=${Date.now()}`; window.location.href = authUrl; } catch (error) { console.error('Error initiating PayPal login:', error); setError(error.message || 'Failed to initiate PayPal login'); } }; const handleDisconnect = async () => { setIsLoading(true); setError(''); setSuccessMessage(''); try { const response = await axios.post( 'http://dev.gotasker.com.au:4000/api/paypal/disconnect', {}, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}`, }, } ); if (response.data.success) { await updateUserProfile({ paypalEmail: null }); setSuccessMessage('PayPal account disconnected successfully'); } } catch (error) { setError('Failed to disconnect PayPal account'); } finally { setIsLoading(false); } }; };
已排查的点
- 确认了Client ID和Secret ID是Sandbox环境的正确值
- 三重核对了PayPal开发者后台配置的返回URI和代码中的
REDIRECT_URI完全一致 - 日志中能看到授权码
code和state都正常接收到了
麻烦各位帮忙看看哪里可能出问题了,谢谢!
备注:内容来源于stack exchange,提问作者Osman Remzi
相关产品推荐
相关产品推荐

