Google App Script调用PayPal API获AccessToken报‘invalid token’问题排查
嘿,我来帮你排查这个「invalid token」的问题——明明复制对了PayPal的CLIENT_ID和SECRET,在Google App Script(GAS)里调用却报错,大概率是GAS的请求逻辑和原生cURL的默认行为有差异,我整理了几个最常见的坑:
1. Base64编码的细节错了
cURL里的-u CLIENT_ID:SECRET会自动把「客户端ID:密钥」这串文本转成符合HTTP Basic认证要求的Base64编码,但GAS里得手动处理,而且容易踩两个小坑:
- 不要在冒号
:前后加空格,比如CLIENT_ID : SECRET这种写法会导致编码无效 - 必须用GAS自带的
Utilities.base64Encode,且确保是UTF-8编码(这个方法默认就是,但别自己瞎写编码逻辑)
正确的编码写法:
const authHeader = "Basic " + Utilities.base64Encode(`${CLIENT_ID}:${SECRET}`);
2. 请求头和Payload格式不匹配
PayPal的Token接口强制要求:
- 请求头
Content-Type必须是application/x-www-form-urlencoded - 请求体必须是
grant_type=client_credentials这个纯文本格式,不能写成JSON
很多人会顺手把Payload写成JSON对象(比如{grant_type: "client_credentials"}),这会直接触发「invalid token」错误。正确的Payload写法二选一:
// 写法1:直接字符串 const payload = "grant_type=client_credentials"; // 写法2:用URLSearchParams更稳妥(避免特殊字符转义问题) const params = new URLSearchParams(); params.append("grant_type", "client_credentials"); const payload = params.toString();
同时一定要在请求配置里明确指定Content-Type:
const options = { headers: { "Authorization": authHeader, "Content-Type": "application/x-www-form-urlencoded" }, // 其他配置... };
3. 沙箱/生产环境搞混了
PayPal的沙箱和生产环境是完全隔离的:
- 沙箱凭证只能请求沙箱接口:
https://api-m.sandbox.paypal.com/v1/oauth2/token - 生产凭证只能请求生产接口:
https://api-m.paypal.com/v1/oauth2/token
如果用沙箱凭证请求生产接口(或者反过来),哪怕ID和Secret是对的,也会返回无效token错误。
4. 开启错误详情排查
建议在GAS的请求配置里加上muteHttpExceptions: true,这样就能看到PayPal返回的完整错误响应(比如具体是认证失败还是参数错误),而不是只看到模糊的「invalid token」提示:
const options = { method: "POST", headers: {/*...*/}, payload: payload, muteHttpExceptions: true // 关键!开启后能看到完整错误信息 };
完整的正确示例代码
把上面的要点整合起来,一个能正常工作的GAS函数应该是这样的:
function getPayPalAccessToken() { const CLIENT_ID = "你的PayPal客户端ID"; const SECRET = "你的PayPal密钥"; const API_URL = "https://api-m.sandbox.paypal.com/v1/oauth2/token"; // 切换环境请修改这里 // 生成正确的认证头 const authHeader = "Basic " + Utilities.base64Encode(`${CLIENT_ID}:${SECRET}`); // 构造符合要求的请求体 const payload = "grant_type=client_credentials"; const options = { method: "POST", headers: { "Authorization": authHeader, "Content-Type": "application/x-www-form-urlencoded" }, payload: payload, muteHttpExceptions: true }; const response = UrlFetchApp.fetch(API_URL, options); const responseData = JSON.parse(response.getContentText()); if (responseData.access_token) { console.log("成功获取AccessToken:", responseData.access_token); return responseData.access_token; } else { console.error("详细错误信息:", responseData); throw new Error("获取AccessToken失败:" + responseData.error_description); } }
内容的提问来源于stack exchange,提问作者Paolo177
相关产品推荐
相关产品推荐

