PayPal REST API - 沙盒返回401的API请求,但访问令牌成功

5

我正在使用Java中的PayPal REST API向测试(sandbox)服务器发出请求以获取访问令牌,这是成功的,然后将该访问令牌发送回同一沙盒服务器进行付款,但却失败了,返回了401未经授权的错误。

我遵循了在此处找到的PayPal REST API说明:https://developer.paypal.com/webapps/developer/docs/api/#authentication--headershttps://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/

我尝试使用OAuthTokenCredential对象来提供我的clientIdclientSecret,但它只返回访问令牌,而没有PayPal提供的appID或以分钟为单位的expiresIn时间。此外,它还抱怨我的类路径中缺少sdk_conf.properties文件。

然后我创建了自己的类,使用Apache的HttpClient进行调用,以获取完整的响应结果,这种方法可行:

为RestEasy定制PayPalAPI接口

@Path("v1/oauth2/token")
public interface PayPalOAuthAPI {
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public OAuthInfo requestAccessToken(
        @HeaderParam("Authorization") String basicEncodedToken, 
        String requestBody
    );
}

自定义OAuth令牌请求

private OAuthInfo getOAuthInfo() {
    OAuthInfo info = null;

    PayPalOAuthAPI client = HttpClientFactory.createRestEasyClient(80, 443, 120, 2,
                                          API_ENDPOINT, PayPalOAuthAPI.class, true);
    info = client.requestAccessToken("Basic "+
                                    generateBase64String(PP_API_USER, PP_API_SECRET),
                                    "grant_type=client_credentials");

    if (info == null) {
        throw new RuntimeException(APIResultCodes.REMOTE_UNAUTHORIZED, 
                                   "Could not authenticate with PayPal.");
    }
    return info;
}

这一切都运作正常,并返回了一个200 OK状态,其中包括所需的JSON响应和访问令牌。
然后,我将该令牌提供给我的“Payment”对象,该对象使用PayPal的代码与其服务器通信(在幕后,我相信它是基本的java.lang.net.HttpConnection代码),但返回了一个401未经授权的错误。
使用PayPal的REST API进行付款调用。
// get a new token
String accessToken = getOAuthInfo().getAccessToken();

// configProps is a Properties object pre-populated with sdk_conf.properties
// values except service.EndPoint, clientID, and clientSecret
configProps.setProperty("service.EndPoint", API_ENDPOINT);

// ... transaction objects omitted

try {
    Payment payment = new Payment();
    Payment.initConfig(configProps);
    payment.setIntent(Intent.sale.toString());
    payment.setPayer(payer);
    payment.setTransactions(transactions);

    Payment createdPayment = payment.create(accessToken);
} catch (PayPalRESTException ex) {
    LOG.error("Failed to submit donation", ex);
}

日志输出

(包括已隐藏标识的HttpClient请求/响应)

2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.impl.conn.DefaultClientConnection Sending request: POST /v1/oauth2/token HTTP/1.1
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "POST /v1/oauth2/token HTTP/1.1[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Content-Type: application/x-www-form-urlencoded[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Accept: application/json[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Authorization: Basic QWM1UmNoQ3g3dHB1bWZSZW9rcUR3MW41bHRXaktQU0xRODIyWUJWWXpEdXpZUGJuc0J0eDZYWGlqX1pROkVJVnQ5eERmM2JnQmw1OG5KYlZ2VmtSR3JCaVZVN1BIWGtSV01mQjVqb3NxRTNkbWxCcF9TV05BdU91eA==[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Accept-Encoding: gzip, deflate[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Content-Length: 29[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Host: api.sandbox.paypal.com[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "Connection: Keep-Alive[\r][\n]"
2013-08-19 16:34:11,077 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "User-Agent: Apache-HttpClient/4.2 (java 1.5)[\r][\n]"
2013-08-19 16:34:11,078 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "[\r][\n]"
2013-08-19 16:34:11,078 [qtp1005310362-44] DEBUG:org.apache.http.wire >> "grant_type=client_credentials"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "HTTP/1.1 200 OK[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "Server: Apache-Coyote/1.1[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "Date: Mon, 19 Aug 2013 20:34:11 GMT[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "PayPal-Debug-Id: d29c41eb8625a[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "Content-Type: application/json[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "Content-Length: 282[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44] DEBUG:org.apache.http.wire << "[\r][\n]"
2013-08-19 16:34:12,690 [qtp1005310362-44]     2013-08-19 16:34:12,691 [qtp1005310362-44] DEBUG:org.apache.http.impl.client.DefaultHttpClient Connection can be kept alive indefinitely
2013-08-19 16:34:12,691 [qtp1005310362-44] DEBUG:org.apache.http.wire << "{"scope":"https://api.paypal.com/v1/payments/.* https://api.paypal.com/v1/vault/credit-card https://api.paypal.com/v1/vault/credit-card/.* openid","access_token":"RVATuLOQB0WFX
    keSDZQw4ZnyfIduPCF2j7sMhcfspwo","token_type":"Bearer","app_id":"APP-80W284285Q519543T","expires_in":28800}"
2013-08-19 16:34:12,693 [qtp1005310362-44] DEBUG:org.apache.http.impl.conn.PoolingClientConnectionManager Connection [id: 5][route: {s}->https://api.sandbox.paypal.com] can be kept alive indefinitely
2013-08-19 16:34:12,693 [qtp1005310362-44] DEBUG:org.apache.http.impl.conn.PoolingClientConnectionManager Connection released: [id: 5][route: {s}->https://api.sandbox.paypal.com][total kept alive: 1; route allocated: 1 of 2; total allocated: 1 of 2]
2013-08-19 16:34:15,197 [qtp1005310362-44] ERROR:com.example.gateways.paypal.payments.PayPalPaymentsProREST Failed to submit donation
    com.paypal.core.rest.PayPalRESTException: Error code : 401 with response : Server returned HTTP response code: 401 for URL: https://api.sandbox.paypal.com/v1/payments/payment

我注意到API发送给我的范围和URL是两个不同的东西。也许我缺少一些配置,以便将API调用发送到沙盒URL?
在上面的代码中,您可以看到我将service.EndPoint设置为API_ENDPOINT,而API_ENDPOINT在我的测试中设置为https://api.sandbox.paypal.com。我在我的属性对象中也没有提供clientIDclientSecret,但是即使提供它们也会得到401错误。
我还缺少什么?

这里也有同样的问题。找到任何解决方案了吗? - Jeremy Burton
没有官方解决方案。我切换回使用官方的OAuthTokenCredentials对象,这样可以工作(但我无法访问APP_ID或expires_in时间)。然后我又改回了自己的OAuthUtil对象,这样就可以进行捐赠了。然而,每次捐赠都失败了,要么是500错误、超时,要么是返回null。现在我正在尝试使用Classic API。 - BrionS
1个回答

0

这看起来像是PayPal的Classic API。你是否在使用REST API? - Alan
我在这里有些犹豫了。事实证明,您不能混合使用Classic和REST,因此无法创建代表另一个帐户(依赖于经典权限API)的rest API应用程序。这里要记住的主要观点是,如果您尝试使用REST API代表其他人发出请求,则可以获取访问令牌,但在尝试创建付款时会收到401错误。 - Ben Cull
正确。据我所知,即使您完成了完整的OAUTH委派过程,REST API也不允许您代表另一个用户发出请求。您必须使用Classic API来代表其他方发出请求。正如您所指出的,REST和Classic的应用程序域不同,并且需要进行单独的验证过程。 - Alan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接