PayPal REST API .net SDK - 400 Bad Requests

梦想的初衷 提交于 2019-12-03 10:53:25
lance

I only started messing with the SDK and API today and ran into this issue right away. I mean, if I'm going to create my own form to handle payments, I'd like to give my user's feedback if anything went wrong.

In any case, I did find some hidden info in the inner exception. Maybe this will help.

catch (PayPal.Exception.PayPalException ex)
{
    if (ex.InnerException is PayPal.Exception.ConnectionException)
    {
        context.Response.Write(((PayPal.Exception.ConnectionException)ex.InnerException).Response);
    }
    else
    {
        context.Response.Write(ex.Message);
    }
}

The resulting response:

{"name":"VALIDATION_ERROR","details":[{"field":"payer.funding_instruments[0].credit_card.number","issue":"Must be numeric"}],"message":"Invalid request - see details","information_link":"https://developer.paypal.com/webapps/developer/docs/api/#VALIDATION_ERROR","debug_id":"0548e52ef9d95"} 
Jonathan Black

Since no one seems to know the answer to this, I dug into the source code of PayPal's .NET SDK for REST API. From this review, it appears that as of the current version, there is no provision for returning the error messages when any 4xx or 5xx HTTP status code is returned by the server. I referenced the answers to this question and have altered the SDK to allow for returning the error response when applicable. Here's the relevant portion of HttpConnection.cs.

catch (WebException ex)
{
    if (ex.Response is HttpWebResponse)
    {
        HttpStatusCode statusCode = ((HttpWebResponse)ex.Response).StatusCode;
        logger.Info("Got " + statusCode.ToString() + " response from server");
        using (WebResponse wResponse = (HttpWebResponse)ex.Response)
        {
            using (Stream data = wResponse.GetResponseStream ())
            {
                string text = new StreamReader (data).ReadToEnd ();
                return text;
            }
        }                           
    }
    if (!RequiresRetry(ex))
    {
        // Server responses in the range of 4xx and 5xx throw a WebException
        throw new ConnectionException("Invalid HTTP response " + ex.Message);
    }                       
}

Of course, this requires changes to the calling function to properly interpret the error response. Since I'm only using the Payment Create API, I took some shortcuts that wouldn't work for general use. However, the basic idea is that I created PaymentError and Detail classes, then altered the Payment.Create and PayPalResource.ConfigureAndExecute methods to populate and pass back a PaymentError object.


Three years later and there is some improvement in PayPal's API error response handling. Because of some other issues I had to re-work my application, and rip out the prior code. I've found that you can now trap for a PayPal.Exception.PaymentsException, then deserialize the JSON Response string into a PayPal.Exception.PaymentsError object.

Catch ex As PayPal.Exception.PaymentsException
    Dim tmpPmtErr As PayPal.Exception.PaymentsError = _
        JsonConvert.DeserializeObject(Of PayPal.Exception.PaymentsError)(ex.Response)

Thanks to @lance in the other answer for the heads-up to examine the exceptions more closely. I attempted to use that solution, but could not make it work.

This seems to have changed, and while many of these answers point in the right direction, none of them worked exactly for me. This is my equivalent of the currently most upvoted answer:

catch (PayPal.PaymentsException ex)
{
    context.Response.Write(ex.Response);
}

with the response:

{"name":"VALIDATION_ERROR","details":[{"field":"start_date","issue":"Agreement start date is required, should be valid and greater than the current date. Should be consistent with ISO 8601 Format"}],"message":"Invalid request. See details.","information_link":"https://developer.paypal.com/docs/api/payments.billing-agreements#errors","debug_id":"8c56c13fccd49"}

@Jonathan, The REST API has log4net integrated and will output the full error response to a log file if you set it up. You must add this config section to your web.config:

<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>

and also set the options:

<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
  <file value="rest-api.log"/>
  <appendToFile value="true"/>
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] %message%newline"/>
  </layout>
</appender>
<root>
  <level value="DEBUG"/>
  <appender-ref ref="FileAppender"/>
</root>

Open your global.asax and add this into the Application_Start:

log4net.Config.XmlConfigurator.Configure();

Now, as long as there are write permissions set on your log folder, then the PayPal REST SDK will spit out error messages to the log file.

My problem was that my total, detail.subtotal, and items.price were passing in values like $1,000.00 and it needs decimal so use something like:

total = Regex.Replace(model.OrderTotal, "[^0-9.]", "")

Improving the solution here is... Simply (Imports PayPal)

    Try
    Catch ex As PaymentsException
        For Each i In ex.Details.details
            Response.Write(i.field) 'Name of Field
            Response.Write(i.code) 'Code If Any
            Response.Write(i.issue) 'Validation Issue
        Next
    End Try

All of the above solution of dont really grasp the problem ..

I ran in to a similar problem a while back .. after debugging my code i found out that the transaction that i was making was logically wrong.. the subtotal was off be $0.25 and paypal server saw it and refused to process it, and gave me 400 bad request

user3609351

If you are using a globalization outside the US you need to convert your decimals to a InvariantCulture:

order.GetTotal().ToString("N2", CultureInfo.InvariantCulture)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!