DEV Community

catrina
catrina

Posted on • Originally published at catrina.me on

400 Bad Request For Declined Payment

[TLDR: successful JSON post, but if credit card is declined by gateway, they return 400 bad request. this sets off exceptions in .NET's HTTP response]

I coded a few weeks ago a .NET post to the PayTrace API which helps me demo and test payment by credit card using client side encryption. The process more or less went like this:

  • Create demo account as a merchant on PayTrace
  • Download PEM key
  • On submit of form with credit card information, an imported PayTraceJS library encrypts the credit card number and csc code
  • Use the demo account’s username and password to submit a request for a token
  • Submit transaction (which includes encrypted info as well as other required fields) using token and await response

A successful HTTP response returns a status code of 200. I read it via stream, deserialize it using JSON into my CardResponse object (both successful and failure responses have the same design). Everything went great until I began testing rejected cards.

My initial demo code that worked lovely when the response was 200 looked something like this:

var httpResponse  = (HttpWebResponse)req.GetResponse();

using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
  string result = streamReader.ReadToEnd();
  creditCardResponse = JsonConvert.DeserializeObject<CardResponse>(result);
}
Enter fullscreen mode Exit fullscreen mode

The problem is that when a card is rejected (either for lack of funds, bad address, whatever the reason) the HTTP response status code changes from 200 to 400 “Bad Request”. This causes an immediate exception to be thrown on the first line in the sample above. Even worse, the 400 code is a bit misleading because my initial thought was my JSON request was malformed. I was missing a required field, maybe? No.

This is by PayTrace design.

If you check their documentation and their sample of a declined response, it states clearly: “Following response will be returned with a HTTP Status 400 Bad Request.

I initially tested this by going through debug mode, going as far as getting the token (I obviously was hitting the PayTrace wall and passing auth to get a Bad Request instead of Not Authorized). Then, I grabbed the token out of my debug session, ran a few more lines, and got the JSON of my HttpRequest. I gathered all info I needed for the request and threw it into Postman. Postman came back with a JSON string, and with all the details I needed: success (failed), response message, etc etc. I’d narrowed the problem directly linked to how I was bringing that response into .NET HttpResponse.

So what now? My HttpResponse was failing immediately on the 400, meaning I couldn’t dig into and read the stream, much less get the reasons why this particular credit card was declined.

My first attempts I tried simple try/catches, but didn’t get it just right until I captured that particular web exception. My adaptation of the code:

try
{
  HttpWebResponse response = (HttpWebResponse)req.GetResponse();

  using (var streamReader = new StreamReader(response.GetResponseStream()))
  {
    string result = streamReader.ReadToEnd();
    creditCardResponse = JsonConvert.DeserializeObject<CardResponse>(result);
  }
}
catch (WebException ex)
{
  using (WebResponse resp = ex.Response)
  {
    using (Stream data = resp.GetResponseStream())
    {
      StreamReader sr = new StreamReader(data);
      string result = sr.ReadToEnd();
      creditCardResponse = JsonConvert.DeserializeObject<CardResponse>(result);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

I’ll have to modify this for other sorts of responses (not 200 or 400), but it was the push I needed to get over this one. I was able to deserialize the failed response into my CardResponse object and report a failure with reasons why.

Latest comments (0)