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.

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.