【问题标题】:Paypal REST api call works from cURL but not from C# codePaypal REST api 调用适用于 cURL,但不适用于 C# 代码
【发布时间】:2013-05-29 01:41:48
【问题描述】:

我正在尝试从我的代码中调用 Paypal api。我设置了沙盒帐户,当我使用 curl 时它可以工作,但我的代码工作方式不同,而是返回 401 Unauthorized。

这里的 curl 命令是 documented by Paypal

curl https://api.sandbox.paypal.com/v1/oauth2/token -H "Accept: application/json" -H "Accept-Language: en_US" -u "A****:E****" -d "grant_type=client_credentials" 

更新:显然.Credentials 并不能解决问题,而是手动设置Authorization 标头(参见代码)

这是代码(精简到其本质):

  HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://api.sandbox.paypal.com/v1/oauth2/token");
  request.Method = "POST";
  request.Accept = "application/json";
  request.Headers.Add("Accept-Language:en_US")

  // this doesn't work:
  **request.Credentials = new NetworkCredential("A****", "E****");**

  // DO THIS INSTEAD
  **string authInfo = Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("A****:E****"));**
  **request.Headers["Authorization"] = "Basic " + authInfo;**

  using (StreamWriter swt = new StreamWriter(request.GetRequestStream()))
  {
    swt.Write("grant_type=client_credentials");
  }

  request.BeginGetResponse((r) =>
  {
    try
    {
       HttpWebResponse response = request.EndGetResponse(r) as HttpWebResponse; // Exception here
       ....
    } catch (Exception x)  { .... } // log the exception - 401 Unauthorized
  }, null);

这是Fiddler(原始)捕获的代码的请求,由于某种原因没有授权参数:

POST https://api.sandbox.paypal.com/v1/oauth2/token HTTP/1.1
Accept: application/json
Accept-Language: en_US
Host: api.sandbox.paypal.com
Content-Length: 29
Expect: 100-continue
Connection: Keep-Alive

grant_type=client_credentials

【问题讨论】:

  • 接受标头中缺少一个空格,但我看不到任何其他明显的内容。您是否尝试过捕获这两个请求以查看有什么不同,例如使用wireshark或Fiddler等代理?
  • @Rup 我尝试使用 Fiddler,仍然无法捕获 curl 请求,但代码请求不包含 Auth 标头(请参阅更新)
  • 是的,一些 HTTP 库,例如除非远程服务器要求,否则 Apache 不会发送凭据,但我不知道 .NET 也会发送凭据。或者至少它应该与他们一起回复 401。可能有办法在请求对象上强制它?
  • 有一个令人不快的解决方法in this old answer:构建您自己的基本身份验证标头。或者我在想HttpWebRequest.PreAuthenticate
  • @Rup 是的,我发现并解决了这个问题。感谢您调查它

标签: c# paypal


【解决方案1】:

Paypal 已弃用 TLS 1.1,现在只接受 1.2。不幸的是,.NET(4.7 之前的版本)默认使用 1.1,除非您另外配置。

您可以使用此行打开 TLS 1.2。我建议把它放在Application_Startglobal.asax

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

【讨论】:

    【解决方案2】:

    我也因缺少示例代码以及响应错误和代码的各种问题而苦恼。

    我是 RestClient 的忠实粉丝,因为它对集成和不断增长的 RESTful API 调用有很大帮助。

    我希望这个使用 RestSharp 的小 sn-p 代码可以帮助某人:-

            if (ServicePointManager.SecurityProtocol != SecurityProtocolType.Tls12) ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // forced to modern day SSL protocols
            var client = new RestClient(payPalUrl) { Encoding = Encoding.UTF8 };
            var authRequest = new RestRequest("oauth2/token", Method.POST) {RequestFormat = DataFormat.Json};
            client.Authenticator = new HttpBasicAuthenticator(clientId, secret);
            authRequest.AddParameter("grant_type","client_credentials");
            var authResponse = client.Execute(authRequest);
            // You can now deserialise the response to get the token as per the answer from @ryuzaki 
            var payPalTokenModel = JsonConvert.DeserializeObject<PayPalTokenModel>(authResponse.Content);
    

    【讨论】:

      【解决方案3】:

      希望以下代码对仍在寻找小块蛋糕以连接到 PayPal 的任何人有所帮助。

      和许多人一样,我一直在投入大量时间试图让我的 PayPal 令牌访问但没有成功,直到我发现以下情况:

      public class PayPalClient
      {
          public async Task RequestPayPalToken() 
          {
              // Discussion about SSL secure channel
              // http://stackoverflow.com/questions/32994464/could-not-create-ssl-tls-secure-channel-despite-setting-servercertificatevalida
              ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
              ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
      
              try
              {
                  // ClientId of your Paypal app API
                  string APIClientId = "**_[your_API_Client_Id]_**";
      
                  // secret key of you Paypal app API
                  string APISecret = "**_[your_API_secret]_**";
      
                  using (var client = new System.Net.Http.HttpClient())
                  {
                      var byteArray = Encoding.UTF8.GetBytes(APIClientId + ":" + APISecret);
                      client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
      
                      var url = new Uri("https://api.sandbox.paypal.com/v1/oauth2/token", UriKind.Absolute);
      
                      client.DefaultRequestHeaders.IfModifiedSince = DateTime.UtcNow;
      
                      var requestParams = new List<KeyValuePair<string, string>>
                                  {
                                      new KeyValuePair<string, string>("grant_type", "client_credentials")
                                  };
      
                      var content = new FormUrlEncodedContent(requestParams);
                      var webresponse = await client.PostAsync(url, content);
                      var jsonString = await webresponse.Content.ReadAsStringAsync();
      
                      // response will deserialized using Jsonconver
                      var payPalTokenModel = JsonConvert.DeserializeObject<PayPalTokenModel>(jsonString);
                  }
              }
              catch (System.Exception ex)
              {
                  //TODO: Log connection error
              }
          }
      }
      
      public class PayPalTokenModel 
      {
          public string scope { get; set; }
          public string nonce { get; set; }
          public string access_token { get; set; }
          public string token_type { get; set; }
          public string app_id { get; set; }
          public int expires_in { get; set; }
      }
      

      这段代码对我来说效果很好,希望对你也一样。学分属于 Patel Harshal,他发布了他的解决方案 here

      【讨论】:

      • 谢谢!你节省了我一半的时间。
      【解决方案4】:

      这可以使用 HttpClient... 'RequestT' 是 PayPal 请求参数的泛型,但未使用。使用了“ResponseT”,它是 PayPal 根据其文档的响应。

      'PayPalConfig' 类使用 ConfigurationManager 从 web.config 文件中读取 clientid 和 secret。 要记住的是将 Authorization 标头设置为“Basic”而不是“Bearer”,并正确构造具有正确媒体类型(x-www-form-urlencoded)的“StringContent”对象。

          //gets PayPal accessToken
          public async Task<ResponseT> InvokePostAsync<RequestT, ResponseT>(RequestT request, string actionUrl)
          {
              ResponseT result;
      
              // 'HTTP Basic Auth Post' <http://stackoverflow.com/questions/21066622/how-to-send-a-http-basic-auth-post>
              string clientId = PayPalConfig.clientId;
              string secret = PayPalConfig.clientSecret;
              string oAuthCredentials = Convert.ToBase64String(Encoding.Default.GetBytes(clientId + ":" + secret));
      
              //base uri to PayPAl 'live' or 'stage' based on 'productionMode'
              string uriString = PayPalConfig.endpoint(PayPalConfig.productionMode) + actionUrl;
      
              HttpClient client = new HttpClient();
      
              //construct request message
              var h_request = new HttpRequestMessage(HttpMethod.Post, uriString);
              h_request.Headers.Authorization = new AuthenticationHeaderValue("Basic", oAuthCredentials);
              h_request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
              h_request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en_US"));
      
              h_request.Content = new StringContent("grant_type=client_credentials", UTF8Encoding.UTF8, "application/x-www-form-urlencoded");
      
              try
              {
                  HttpResponseMessage response = await client.SendAsync(h_request);
      
                  //if call failed ErrorResponse created...simple class with response properties
                  if (!response.IsSuccessStatusCode)
                  {
                      var error = await response.Content.ReadAsStringAsync();
                      ErrorResponse errResp = JsonConvert.DeserializeObject<ErrorResponse>(error);
                      throw new PayPalException { error_name = errResp.name, details = errResp.details, message = errResp.message };
                  }
      
                  var success = await response.Content.ReadAsStringAsync();
                  result = JsonConvert.DeserializeObject<ResponseT>(success);
              }
              catch (Exception)
              {
                  throw new HttpRequestException("Request to PayPal Service failed.");
              }
      
              return result;
          }
      

      重要提示:使用 Task.WhenAll() 确保您有结果。

          // gets access token with HttpClient call..and ensures there is a Result before continuing
          // so you don't try to pass an empty or failed token.
          public async Task<TokenResponse> AuthorizeAsync(TokenRequest req)
          {
              TokenResponse response;
              try
              {
                  var task = new PayPalHttpClient().InvokePostAsync<TokenRequest, TokenResponse>(req, req.actionUrl);
                  await Task.WhenAll(task);
      
                  response = task.Result;
              }
              catch (PayPalException ex)
              {
                  response = new TokenResponse { access_token = "error", Error = ex };
              }
      
              return response;
          }
      

      【讨论】:

      • 我得到关于 SSL / TLS 的 AuthenticationException-WebException。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-17
      • 2021-09-06
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      • 2014-12-06
      相关资源
      最近更新 更多