Showing posts with label Solution and Approach. Show all posts
Showing posts with label Solution and Approach. Show all posts

Tuesday, December 16, 2025

Payment Transaction Declined With Code 201, Why?


I read a LinkedIn post yesterday.  This post asks for the appropriate contextual message to deal and how to continue upon what is shown to the user.  I see it is fair and straight.  I expect the same.  

Thanks Liudas Jankauskas for writing this LinkedIn post and sharing your experience.

What surprised me is why the discussion was not taken forward in the post.  As a  result, we did not seed the mindset and attitude of Test Engineering and Prevention discussion.  Testing does not prevent; but, the outcome of the test will persuade the prevention efforts and culture.

Okay! If you see this post is too long to read, then here is in short to you -- This is not a API problemThe API has worked seamlessly and it has done what it is supposed to do.  The code 201 returned is right and it is supposed to be there.  Here, the 201 is not a HTTP Status Code.  It is 3DS Error Response code.

Then, whose problem it is?  To know that, read the entire blog post.  You will thank me and yourself!


I Say This, Before I Start

  1. I'm writing this post as an interpretation and analysis for the JSON shared in the LinkedIn post.
  2. There is no intention of pointing to anyone.
    1. The intention is to share -- how to analyze and have perspectives to analyze when I [a test engineer] experience it.
    2. This post can become a reference to someone who is serious about Test Engineering and Prevention, and Testing.
  3. The intention of this post is to let know,
    1. How to analyze such incidents in the payment context?
    2. Is this a API problem?  What behaviors should be classified as an API problem?
    3. Which component and layer is supposed to handle it?  How and why?
    4. Which other components and layer is supposed to assist to handle it?
    5. How to see and interpret the HTTP and its status code in a narrowed cases?
    6. It is not always the API problem!
  4. I'm open to correct myself, unlearn, learn and update this blog post, when shared why I'm not making sense with I have written here.
    1. I will be thankful and humble to you for helping me to correct myself. 🙏
    2. Be comfortable and do connect and help me if you see I need it.


The Payment Failed; Could Not Buy Ferry Ticket

The user wanted to buy the ferry tickets by making the online payment.  The payment did not happen and could not buy the ferry ticket.

Instead, the user sees the JSON on the UI. 

The JSON has error description which reads as -- "TdsServerTransID is not received in Cres."

To understand what is TdsServerTransID and Cres, it is required to understand the payment gateway flows.   Continue reading the next sections to know them.


How It Is Layered and Works?


Before I get into analysis and say where the problem is, this context requires an understanding of the payment gateway flow.  Having this understanding, it helps to know how it could have been handled and prevented.

Refer to this below pic.  It gives the sequences of interactions in the payment transaction.  Note that, the below pic is not complete.  It is kept to what is needed to the context of this blog post.


Transaction between 3DS Server and Bank for the Challenge through
POST request.  It is shown as CReq and CRes.


The Sequence of Interactions
  1. I initiate the payment on merchant website or app to buy the ferry ticket.
  2. The merchant creates a transaction id for tracking.  Then, it hands over the rest to payment gateway.
  3. The payment gateway uses the 3DS way to carry this transaction.
  4. The 3DS initiates the Authentication Request (AReq) which will pass through the Directory Server.
    • The Directory Server reads the data in the request and forwards it to the right bank who have issued the card [or account] used in the payment.
  5. The bank receives the AReq.  
    • The bank decides should it give a challenge to the user to make the payment or just agree with the data passed in AReq
  6. Now, in this case the bank has decided to give the challenge to the user.  
    • The bank lets know the 3DS server through the response ARes.
  7. The challenge usually is to enter the OTP received over a SMS and authenticate.  I presume the same in this case.
  8. The user enters the OTP and POSTS the request.  Let us call this request as CReq.  The CReq is sent from from the user's browser or app to the bank.  The user interface on the browser or app at this point is handled by the payment gateway and not the merchant's web or app where the ticket is being brought.
    • This CReq will have,
      1. tdsServerTransactionId
      2. messageVersion
      3. messageType
      4. challengeWindowSize
      5. ascTransactionId
    • Note that, the CReq having the tdsServerTransactionId which is generated by the payment gateway.  tdsServerTransactionId is required in the CReq and expected by the bank.
  9. On receiving, the CReq, the bank processes the request.  The bank responds back to the 3DS server; let us call it CRes.
  10. The CRes will have,
    1. tdsServerTransactionId
    2. acsTransactionId
    3. challengeCompletionId
    4. transStatus
    5. messageType
    6. messageVersion
  11. If all goes as expected, the authorization will be given for the payment.  
    • If not, the bank responds to the payment gateway (3DS) that there is a problem and the message.
      • It is the payment gateway which has to read this problem and message.
      • The payment gateway has to give the better contextual information through its interface to the merchant who is consuming the service.
      • This is key!
      • Hope you found the spot here!


The Code 201 


Maybe, we test engineers presume the error code 201 returned is HTTP Status Code.  Ask your team what is this code and what is the purpose of returning in the JSON.

Back to 201, here.  It is not a HTTP Status Code.  It is neither a custom developed error code by payment gateway nor merchant who is selling the ferry ticket.

This is a payment domain specific code used in the payment technology.  Yeah!  The code to tell the payment gateway and merchant what happened with the initiated payment transaction.  

To be particular and more specific to the context, the 201 here is a 3DS APIs response code.


The 3DS Error Code and its Description.
The credit of this image is to developer.ravelin.com

The above image is shared here so that you know the error codes are available in the payment context.  I'm aware of 3DS protocol hence I could relate and understand the error code 201.  Refer to the References section at the end of this blog post.

The payment gateway has to read this error code. Then provide the right contextual message and instruction to merchant saying what to do incase of payment failure.

Here is the catch.  Not all merchant develops a mechanism to handle this.  Instead, it is left to payment gateway service provider and depend on it.  

Do the merchant bother about handling it at their end? 
  • As a merchant, I can switch to another payment gateway tomorrow.  
  • Why should I invest in building, developing and maintaining this as a part of my business when I'm paying another business to do it for me?  
  • Isn't that senseful business question or decision?
Now, you tell me who is expected to handle this 201 error? Who should let me know on the merchant web user interface what to do?



The Case and My Initial Observations


On submitting the challenge given by the bank, the below details is shown to the user.  


Pic used from the post of Liudas.
It is the CRes returned from Bank to 3DS Server at Payment Gateway.

My Observations
  1. Looks like the ticket being bought and payment made was in Turkey.
  2. The payment gateway involved to complete the payment transaction is Payten.
  3. The user is tryin to buy the ferry tickets and is making online payment. This error is seen on submitting the challenge given by the bank,
  4. Looks like the card is used for the payment.
  5. In the LinkedIn post, I read, multiple attempts were made to make payment, and the same message is seen.
  6. The Error Code returned is 201.
    • Which means, a mandatory field is missing and it is a violation.
  7. Error Component is S, which probably means 3DS server.
    • The component that raised the error.
  8. There is an error which is not handled.
    • The error reads as Unexpected error.
    • There is an id to track it.  That is, for auditing and tracking by the payment gateway.
    • This id do not have anything to do with the bank and merchant who is selling the ferry tickets.
  9. The message has a version that reads as 2.1.0
    • This means, 3DS protocol used is of version 2.1.0
    • This is communicated by 3DS server to the Directory Server and Bank Server, so that they use the same version in the contract.
      • If the same version is not available at the bank server side, the lower version will be used.
  10. Error description reads, the TdsServerTransID is not received in the CRes.
    • The 3DS component is raising this error.
    • TdsServerTransID means ThreeDS Server Transaction Id
    • 3DS means Three-Domain Secure, which is a security protocol used for online card transactions.
  11. This means, the response from bank is, it cannot fulfil the payment request.
    • Because, the request from the 3DS layer do not have TdsServerTransID.
    • Wait!
      • Do I actually know if the CReq had the TdsServerTransID in the payload?
      • This is critical to know.
    • This is important to know.
      • The AReq (Authentication Request) will have the TdsServerTransID in its payload.
      • That means, this was present earlier.  So, the CReq is fired from the 3DS server on receiving the ARes.
      • The bank received TdsServerTransID and said it is giving a challenge  before proceeding to authorization for payment.
        • This response (ARes) from the bank will have TdsServerTransID.
          • Along with this, the below are also present as a correlation ID to track this transaction.
            • dsTransID
              • The Directory Server ID to track this transaction
            • acsTransID
              • The bank server ID to track this transaction
      • From here, the CReq payload requires the TdsServerTransID and acsTransID
        • Where did the TdsServerTransID go missing now?  How?  Why?
        • This needs to be test investigated.  I have shared possible causes in the next section.

The TransactionID of the merchant who is selling the ferry ticket is different from the TdsServerTransID.
    • The TdsServerTransID is needed for the bank and Payment Gateway to complete the transaction.
    • Without this TdsServerTransId, the bank cannot proceed to authorize the payment.

All this is happening between the payment gateway [3DS server] and the Bank.  The user and the merchant website is not in the picture here.



Why Did It Happen?


It happened because, the CReq from the 3DS Server of payment gateway did not have that TdsServerTransID.  That is what the CRes from the bank says.  

Note that, I presume, the bank systems and services are functioning and serving its customer at this point in time.  This is an analyzed assumption I'm making here and I'm aware of this assumption I have made.

What made the CReq to miss the TdsServerTransID?
  1. I read, the user made multiple attempts to initiate and make the payment.
  2. I have multiple perspectives and interpretations with hypotheses to say this could have lead to miss the TdsServerTransID.
    • Talking about each hypotheses in detail is not the scope of this blog post.
    • But, here are some of the possibilities that could have led to this situation.
      • A glitch at payment gateway at that point in time.
      • Someone is breaching in the middle and tweaking the request and response over the network.
      • Device and hardware
      • Geo location
      • Network and traffic
      • Timeouts
      • Latency
      • Storage running out
      • Configuration gone wrong
      • Caching and missing -- not persisting
      • Intermediate and dependency services clogged
      • A new release deployed and updated
      • Downtime in the bank's services
      • Intermittent bank's services
      • Mismatching 3DS protocol versions that is not supported and accepted at either ends
      • And, more!
In simple, it was well handled by the bank.  I assume, the amount is not debited from the user's account.  This is equally important and the LinkedIn post do not share about it.

I see the bank's service have worked well in this context and done what is expected out of it.  The thumb rule is, when there is discrepancy in the data expected and received, do not authorize the payment request; abort it

Can the service do much more if the TdsServerTransID is missed in the payload of AReq?

Me as a test engineer in the payment gateway engineering team, I will test for the below in minimal and as a must,
  1. AReq with no TdsServerTransID and observe how what happens!
  2. Do the 3DS server still fire a AReq to the bank with no TdsServerTransID?
    1. If yes, then that is a problem!  It should be handled here.
    2. This problem can be prevented!
    3. I will ask my team why are we encouraging such request?
      • This increases our customer support cost and operations time in responding to our clients for using our payment gateway.
      • And, merchant can lose the business because of our payment gateway.
  3. What should 3DS server do when initiated AReq is missing the TdsServerTransID?
    1. What all other data in the transaction and session should be retrained and intact?
    2. Will these data change with a fresh TdsServerTransID created?
  4. I will explore and figure out what factors caused the 3DS to lose the user's authentication.


Who Created The Problem?

  1. From the error shared, I see,
    •  It is 3DS server who created this problem presuming the bank and its response is right.  


Is This An API Problem?

  1. No, it is not a API problem.
  2. If we call it as a API problem, we have not understood what is an API.


Whose Problem It Is?

  1. It is the problem of the service that fired the CReq.
    • Because, it fired CReq without the TdsServerTransID which is mandatory key-value in the payload.  
    • TdsServerTransID value cannot be null nor empty in JSON.
It appears to be a business logic problem of the service at 3DS server side for firing CReq with no TdsServerTransID.



How It Can Be Prevented?

  1. By making sure CReq will always have a distinct TdsServerTransID.
  2. If no distinct TdsSevrerTransID in a fresh CReq being fired, abort the request.
    • Create the distinct TdsServerTransID and then construct the request payload before firing the CReq.
To make sure, payment gateway [3DS server] preserves the TdsServerTransID, dsTransID, and acsTransID of the session.  If any one of this not matching at any of the components during the transaction, aborting the transaction is the best and right action to do.  

Can you recall your daily life experience where you are said to not refresh or click on the browser's back button when you have initiated the payment transaction on web or mobile app?  This is the reason!  To preserve the data of the transactions in the session between these systems -- merchant web or app, payment gateway [3DS server], Directory Server and Bank Server.

This is an automation candidate.  It has to be part of the daily test runs in the automation.



Who Should Be Fixing It?

  1. In my opinion for today upon analysis and assumption I have made, it should be fixed by the payment gateway.
  2. The payment gateway has to interpret the 3DS error code returned by the bank.  
    • And, then initiate the appropriate action as the payment transaction has failed. 
  3. A new transaction between the 3DS and bank has to be started for that merchant's order.  
    • If it cannot happen, the payment gateway should abort all the current open session tied to that merchant's order.
    • And, let know the user what is happening, and then direct the user.
  4. The payment gateway can read the ARes and CRes.
    • That means, the payment gateway can read the HTTP Status Code of ARes and CRes.
    • If there is an error code in ARes and CRes,
      • Then, the gateway should assert for the 3DS error code along with the HTTP Status Code.
      • For example, 
        • Say, the HTTP Status Code is 400 and 3DS Error Code 201 in CRes.
          • Assert for these two and direct with appropriate contextual message and direction.
          • In general, this is how custom developed error codes are handled by the client on receiving it from the services.
Note that, it is Error Code and not the Response Code.  The two are different.  And, the HTTP Status code is different from these two.



Why It Is Not An API Problem?


API is an interface which exposes the available services to the consumer.

It is the services which collects data and build the request payload, and expects the payload to process. 

This request will pass through an interface which is opened to the consumer.  This interface is called an API -- Application Programming Interface.

Analogy,
  • The car has gear stick to switch the gear.
    • This is an interface to the car's gear system.
    • The driver will use the car's gear system through this gear stick.
  • The gear box in the car is a service.
    • The gear box adjusts and responds by switching to the gear per the driver's input.
    • The different operations [business logic] provided by the gear box services are,
      • Reverse
      • Parking
      • Neutral
      • Switching the gear and assisting other components to speed up or speed down the car.

Can the interface have problem?  Yes, it can have.  In that case, the service will not be available to serve or to discover.  
  • Like, if the gear stick has problem, I cannot use the gears of the car, but, the gear box can be fully functional and in working condition.  
  • It is just the interface [gear stick] having the problem and as a result the consumer [driver] is unable to use the services [gear box].

In this case, the payment gateway could fire CReq to the bank.  That is, the API exposing this service is functioning.  It is the problem in a service.  It is the service that has missed to ensure and mandate the presence of TdsServerTransID -- a business logic problem.

In simple, we Software Engineers [including me] use the term API vaguely and with no sense of what it means.  This is my observation.  Further, the Software Testers have tossed this term API in all possible ways and learning it in incorrect ways.  I have no doubt in it when I say this.

Next time, when you say it is a API problem, rethink on what you are saying.  Is it an API or the service(s) which is accessed through that API?  It is useful when we describing the behavior of the system and its layer.  

I cannot say it is a gear box problem for the gear stick not [usable] working, and vice versa.  You see that?




To stop here,
  1. Testing skills and testing will help when it is collaborated with awareness and skills of the tech stack used in building the software system.
  2. Testing skills, programming and tech skills are not enough!
    • The domain skills and awareness is essential and critical.
    • If one is aware of the domain where the problem is observed, then the 201 will not be read as HTTP status code.
  3. Build the domain skills and maintaining the knowledge base as a GitHub project helps in a longer run.
  4. Interfaces and its gateways should not be overloaded with additional responsibilities to make sure the mandatory key-value is present.
    • If did so, one should learn why. Because, it is an anti-pattern and not a suggested software engineering practice.
  5. It is the services that has problems most times.  
    • The interfaces and its gateway will have the discovery, orchestration and traffic problems along with the risks of security.


References:
  1. https://httpstatuses.io/
  2. https://developer.elavon.com/products/3dsecure2/v1/api-reference
  3. https://developer.ravelin.com/psp/api/endpoints/3d-secure/errors/
  4. https://developer.elavon.com/products/3dsecure2/v1/3ds-error-codes
  5. threeDSServerTransID  (in our case the JSON reads as TdsServerTransID)
    • 3DS Server Transaction ID
    • Universally unique transaction identifier assigned by the 3DS server to identify a single transaction.
    • The 3DS server auto-populates and appends this filed value in the authentication request (AReq) it sends to bank in addition to the data you send.
    • This value can also be found by a lookup in the response received by this service -- /3ds2/lookup


Friday, June 27, 2025

Give a Name for the Base64 Image in an Extent Report

 

I read the below question in Testing Mini Bytes community.

Hi All, I am using extents reports in my c# automation framework. I am trying to attach the screenshot but it is always showing with name as base64img. Any idea how to overwrite that base64img to customised text?


Capturing a Screenshot in the Test Run

With the Extent report, one can capture the image of screen during the test run.  This capture can be on certain condition or state being met.  That is, it can be -- on fail of a particular action; in a transition between the states; etc.

In an Extent report, when the image is captured as Base64 image and the thumbnail is set to false, the images captured will have name "base64img".  Clicking on this, I see the respective image.  


No Customized Text as Name for Base64 Image

Say, a Extent report has 10 images captured as Base64 image with thumbnail false.  All these 10 different images will have the same name -- base64img.

  • This can be more chaotic to interpret when a test run has captured more than one Base64 image, and, all have the same name -- base64img.
  • Do you see that? 
    • How do you know what is that image without opening it just by reading base64img?
    • That's hard and you have to make additional clicks to know what is in each image!
      • Is this a better use of time of you who is reading the Extent report?


Naming the Base64 Image in an Extent Report

I see, the semantics in C# should also be similar to what I'm sharing below.  




The above gist should help to have a customized text given for each captured Base64 image.

In this case, the captured Base64 image name will be the customized text "Resend_OTP_Button_Not_Available" and not base64img.  This makes it more contextual and relatable for the reader of an Extent report.



Wednesday, June 18, 2025

Monetary Value Round Off -- A Legal and Business Problem

 

I read a blog post from Gaurav Khurana suggesting on round off of a number having decimals.  This is the post.  What made me curious is that number and what it meant.  It was a discount money having decimals.  Here, the number of decimals was not restricted to two.

Gaurav, said, it is a better experience if that discount amount is restricted to two decimals and rounded up.  Initially, I see, this is a fair expectation.

Here is the pic of a bill which Gaurav has shared.  I'm using it here on his permission.


I woke all of a sudden at 2:18 AM today, and I relooked into the post and that value whose decimal is not restricted.  I said myself there is something which cannot be rounded up or rounded down here and I could interpret it for a context.


Interpretation of the Value and its Meaning

  1. The "Bank Discount" showed the below amount
    • - 450.90999998999996
  2. The amount is in Indian Rupees
  3. This amount tells, it is the discount offered by the Bank on that transaction
  4. On round up, it will be,
    • - 450.91
  5. If I use the round up value, then the "Balance Paid" amount will be,
    • 4,058.18
  6. If I do not use the round up value, then the "Balance Paid" amount is still,
    • 4,058.18
  7. If I do not use the decimal values in "Bank Discount", then Balance Paid is,
    • 4,059.09
      • That is, I will have to pay 0.91 rupee (91 paisa) more.
    • In this case, someone is at loss.  Who?
      • Business?
      • Customer?
        • In the context of this bill, it is, customer who is paying.
        • Customer will pay 91 paisa, which is, almost one rupee more.
          • Now, imagine, the profit which business makes from all its customers for not having that decimal values in the amount!
        • Is this right?
The meaning and value of Balance Paid is not affected, in both cases, that is,
  • When I use Bank Discount value as is with decimals.
  • Or, When rounded up with two decimals.

My question is,
We should have check on decimals shown and used.  But, should we round up the number when it is of monetary significance and value?


Why I have this question?

  • Refer to the below examples.
    • 450.35
      • On round up, it will be 450.5
    • 450.55
      • On round up, it will be 451
If observed, on round up, the amount discounted is in excess by 0.15 rupee, that is, 15 paisa; and, in the case-2, it is in excess by 0.5 rupee, that is, 50 paisa.

If the bank loses these paisa for each transactions of same customer and other customers, won't the bank is shelling out the money?  Is this good?

Likewise, if I have to pay 0.15 paisa and 0.50 paisa, am I not paying more?  Then, imagine the big amount that business is making by rounding up a monetary number and making its customer pay in excess.


My Thoughts on Monetary Value Round Off

  1. The monetary value should not be rounded up or rounded down.  
    1. But, it can have a decimals and limited to two or to what business sees manageable.
      • As Gaurav said, decimal to two digits makes life easier for all is my understanding.
    2. The customer need to see this monetary value.
  2. The generated bill or transaction should have this detail.
  3. When making a payment, the billing system, can show a round off value.
    1. And, the business should decide upon, should the amount be round up or round down
May be to avoid such arguments and discussions in the billing counter, today, the bill amount will be a whole number and not a number with decimal.

Such round up instances can turn to a legal problems for the business.

I have noticed, the business offering round down on a monetary value, at times.  Also, I have noticed, certain consumer mobile apps showing rounded up number without breaking down the bill details.



Whatever, as a Test Engineer, I and you, have to create a report [ticket] for product's reference; keep the stakeholders informed of such behavior in the system by explaining the impact to business in monetary and legal aspects.





Monday, June 16, 2025

Solution Approach - How to Automate to Use the Magic Link for Login?

 

The question asked in The Test Chat community on 14th June 2025 22:42,

Hi All,
Has anyone implemented test automation for an application that uses a magic link for login?
If yes, could you please share the approach you followed?
Thanks in advance🙏🙏


My Analysis and Understanding

  1. The question asked is around automation.
  2. That is, how to automate to use the link (hypertext) generated for login.
  3. There are several ways to approach this, via
    1. GUI
    2. API
    3. Combination of GUI and API
  4. I prefer to use it in combination of GUI and API for first without using any email client services and app.
    • Why?
      • I want to keep it simple, quick and not to complicate the testing problems much more.


Solution and Approach

I see, it is not a complicated Test Engineering problem unless the project and floor do not support.


I understand, it is about using a link (hypertext) to login into a application via automation.

How do you get this hypertext?
  • It should be generated somewhere within the system's boundary.
  • Can you get that generated hypertext without using any mail client and its service points? How?
    • Do you have an endpoint that generates this hypertext?
    • If so, then this endpoint should also have some means to share the generated hypertext for login request?
    • Pick this generated hypertext via an API request in your automation.
  • This should work!  Unless the test goal in the automation is not to open some configured mail client and read the received mail.
    • But, how many times will you do this in automation, if the goal is to open configured mail client?
    • Is this a good use of automation?
If there is an endpoint to GET that generated hypertext (POST), it is a fair simple task.  

Say, there is no endpoint with GET request to give the generated hypertext. Then, look at the response payload of POST request that generates the login hypertext.   Consume this hypertext now and use it in GUI automation to login.  Say, there is no response payload in POST request.  Ask your team to build a GET request to consume internally and maintain it securely so that no external access is allowed.

Let us keep the testing problems simple!

 

Solution Approach - Blog Posts Series

 

I read questions and challenges shared in the Software Testing communities.  I try my best to assist in the ways I can.  

While I do this, I share the solutions and how to approach the solution.  

  • I see, this solution approach is good to be here as well.  
  • So that, it can serve as a reference to the readers who come to Testing Garage.
  • To make this happen, I will be sharing the blog posts with the question asked and possible solution to it, under the tag label "Solution and Approach".

If there is any follow-up discussion by the community and the person who asked the question, I will share those detail in the comment section of the same post.

It will be a simple and quick read blog posts for you, most times.

Have a good time reading, analyzing, debugging and challenging the solution and approaches I share.