Troubleshooting asynchronous transactions

Background
Before starting this tutorial, make sure you understand the following topics from the Quick start section:
  • Sandbox environments (especially about testing).
  • Authentication (about authenticating using your API user and when you need a login versus a one-time-use token).
  • 3DS requirements for important overview information and concepts.
  • 3D Secure in the Getting Started section, for a general overview of the product.

Asynchronous transactions, such as with a 3DS payment flow may entail more complex troubleshooting than a normal payment flow. Therefore, Nexio has provided an API endpoint for tracking the status of an asynchronous transaction. This tutorial and troubleshooting information are meant to provide additional details about how to use it.

For this tutorial, we assume that you have already completed the steps in Using 3DS to run transactions.

📘

Note

In order to effectively process and manage 3DS transactions, you need to be aware of the following:

  1. Nexio strongly recommends that you include an order number with each transaction. Ideally, this value should be unique to the merchant. If you run 3DS transactions and you do not use a unique order number, there will be payment flows that you won't be able to reconcile.
    Nexio recommends a format for the order number of a unique value that also includes an attempt number so that you can track attempts per order, such as when a payment attempt fails. For example, something like [order_number]-[attempt_number].
  2. Nexio highly recommends that you configure and use webhooks. For more information about doing this, see Webhooks.
  3. Nexio suggests that you configure listeners on the merchant website for receiving events. For more information about how this works, see steps 2 and 6 in the Creating a card checkout page with the iframe tutorial.

For 3DS, the payment flow is as follows:

  • If you need to save a payment card:
    • Get a one-time-use token, making sure that 3DS is enabled.
    • Use that one-time-use token to save the payment card.
    • Save the card token for later use.
  • Run the transaction using a saved card token or full card information.
  • Put the 3DS redirect link as the SRC for an iframe or an HTML element such as a link. Save the asyncTraceId to use for tracking the transaction and for troubleshooting.
  • Depending on verification needs, the customer may either get redirected to their bank's 3DS page where they can authenticate or the redirect happens behind the scenes and the bank automatically authenticates the customer.
  • The 3DS process completes and returns control to the merchant site, where you can display a payment confirmation page.

Sending the paymentType

Nexio requires that you send the appropriate paymentType parameter and value in your request to Create one-time-use token. Sending an incorrect value may result in fines to the merchant account from card schemes (Mastercard, Visa, and so forth).

You can see more information about the various states here: Payment type (paymentType) in the Constant transaction values topic.

If you are having trouble with a 3DS transaction, you should check to make sure that the paymentType is being set appropriately for the transaction.

Customer not present

If you use a value of initialMoto, this means that the transaction was captured in a virtual terminal when customer card information was received over the phone, through mail, or via fax. In this case, you may not be able to do a recurring payment later with that card. For subsequent transactions that are supported by the gateway or processor, use scheduled, unscheduledCit, or unscheduledMit.

Customer present (or customer-initiated)

If you use a value of initialScheduled, this means that the customer is present (or they initiate it on a website) and you are doing a transaction that will be the first in a series of transactions on a fixed schedule. Any subsequent transactions (recurring or using the same card) will be scheduled.

You may also use a value of initialUnscheduled if you know that there will not be any future scheduled or recurring transactions (such as an ad hoc, card-on-file transaction). If you are not sure if you will have scheduled transactions, you should still use this type. Any subsequent transactions (using the same card) need to be either unscheduledCit or unscheduledMit.

Later transactions

Any subsequent transactions for that same card need to use a specific paymentType, depending on what it is and what the first paymentType was.

  • Use scheduled if the first one was initialScheduled (customer present) or initialMoto (customer not present).
  • Use unscheduledCit for a customer-initiated transaction and if the first one was initialUnscheduled (customer present) or initialMoto (customer not present).
  • Use unscheduledMit for a merchant-initiated transaction (no 3DS verification will be possible) and if the first one was initialUnscheduled (customer present) or initialMoto (customer not present).

Handling the 3DS flow

When the customer goes to pay, the system determines whether the 3DS flow needs to be used.

📘

Note

If you are using the payment iframe (Run card transaction with iframe), Nexio handles the redirect flow for you.

One of the first checks that your webpage should make is on the response from the payment. If the 3DS payment flow is required, the response includes a "redirect" status. Otherwise, the response is a normal payment response. So, you could have code like the following to check for the redirect status option:

    // making the payment request. `formData` includes the body of the payment request, 
    // with all required parameters
    fetch('http://localhost:3000/pay', {
            method: 'POST',
            body: JSON.stringify(formData),
            headers: {
                'Content-Type': 'application/json'
            }
        }).then(function (response) {
            return response.json();
        }).then((objData) => {
            if (objData.status === "redirect") {
              // set actions such as showing the 3DS iframe, logging the response, 
              // saving the objData.asyncTraceId, setting the SRC of the iframe 
              // as specified in the response
            }
            else {
                // this only happens for non-3DS payment flow
            }
            
        });
        return false;
    }

Note the if clause that is looking for objData.status. A redirect response when 3DS is required looks like the following:

{
  merchantId: '[merchant_id]',
  status: 'redirect',
  message: 'Provide redirect url in iFrame to shopper to complete 3ds authentication and transaction',
  redirectUrl: 'https://api.nexiopaysandbox.com/pay/v3/threeDS/frame?token=[random_token]',
  asyncTraceId: '[random_token]',
  'random-[nnnn]': '[random_value]'
}
{
  error: 446,
  message: '{"response":{"rmsg":"Authentication Failed"}}'
}
{
  error: 409,
  message: 'Either the routing rules (Dashboard Payment Routing and/or paymentOptionTag) filtered out all possible Merchants, or the filtered Merchants do not have this paymentMethod and currency combination configured. Please check your request, edit your Dashboard Payment Routing rules in the Nexio Dashboard Settings or contact integration support.',
}

You may also want an else if set of clauses to capture some errors that may occur as you are getting things set up:

  • An objData.error may be "446". This means that 3DS is not enabled correctly for the account. Contact Integrations support for assistance.
  • An objData.error may be "409". This means that 3DS has not been correctly configured or the data from the transaction is routing the transaction to a connection that does not support 3DS or that the routing rules have filtered out all possible connections and so no payment can be sent. Look on the backend server at your one-time-use token code and your run transaction code to see if any processingOptions, currency, item information, or paymentType is routing the transaction inappropriately for your 3DS flow.

Next, you should also save the asyncTraceId from the redirect response. This can happen at the frontend, but you may also want to do it on your backend server so you can save it to use for tracking and troubleshooting. The following shows potential code that notes the ID:

app.post('/pay', (req, res) => {
  // call separate function to do the payment request
	return runCardTransactionFunction(req.body).then((response) => {
    	// log response information
      console.log('------->run card response', response);
      // check for a redirect response
    	if (response.status === 'redirect') {
          // set a variable to the asyncTraceId and log it
          asyncTraceId = response.asyncTraceId;
          console.log('------->asyncTraceId for troubleshooting', asyncTraceId);
      }
      // return the entire response to the frontend
      return res.json(response);
    })
});
// added to function where payment response gets received
console.log('------->response Data for 3DS redirect', objData);
currentAsyncTraceId = objData.asyncTraceId

// added to listeners
if (message.data.event === 'initiate') {
  console.log('Starting 3DS payment flow for asyncTraceId: ', currentAsyncTraceId);
}

And, if you have Webhooks set up, you can also receive updates about transactions and create backend code to handle those updates with the View transaction async status endpoint and the transactions endpoints, such as View transactions.

3DS states

Part of troubleshooting asynchronous transactions includes understanding the different states that are possible and where problems might occur in each state.

These states start with the server returning that redirect status from a payment request. You can then use the asyncTraceId value with the View transaction async status endpoint to track which state the transaction is in.

processFromApi

The first state that happens with a 3DS transaction is processFromApi.

This state begins with a request to the "Run card transaction" or "Run card transaction with iframe" endpoint and ends with the response with the URL for 3DS redirection.

For this state, the initiatedAt date and time is the moment that the payment request was sent to Nexio.

The async trace state should be redirectUrlReturnedFromGateway, indicating that Nexio returned the URL. If you see any other state, you should contact Integrations support for assistance.

iframe

The next state is iframe.

This state begins when the webpage loads the iframe. It ends when the customer clicks the Confirm button to be redirected to the bank's page. For this flow, the async trace state will be loaded, indicating that the iframe loaded, or submitted, indicating that the customer clicked the Confirm button in the iframe. If, instead, the customer clicks Cancel in the iframe, the system returns a 481 error indicating that the user canceled the request and the state for this step stays at loaded. If the customer never clicks either button, the state stays at loaded. In this situation, you need to check the async trace after one hour. If this state is still at loaded, then you can cancel the order.

However, you can skip this stage by substituting popup for iframe in the redirect URL. You would then either load the popup yourself, or you can provide some other kind of interaction that would open the popup (such as your own button). In this case, the date and time information appears for this state, but is copied from the next state, popup. In this flow, the async trace state will always be submitted.

You may want to include a check on your website to make sure the iframe loads. This will help you troubleshooting errors at that point. You can look for the loaded event or through a call to the View transaction async status endpoint to check that the state for iframe is loaded.

popup

Next is popup.

This state begins when the system opens the bank's 3DS page for customer validation. An event listener for initiate gets sent to the web browser. It ends when the customer completes (or fails) 3DS validation. Validation failure may occur when the customer does not successfully authenticate with the bank, the customer closes the page without validating, or the customer waits more than an hour to validate.

For this state, the initiatedAt date and time is when the system successfully loaded the popup (or a new tab) that the bank has for 3DS validation. The async trace state is set to loaded at this point. When the 3DS authentication step finishes, whether authorized or unauthorized, the state is set to submitted. If the customer exits the popup before completing the 3DS authentication with the bank, the system returns a 481 error indicating that the user canceled the request and the state for this step stays at loaded. If the customer never completes the authentication with the bank, the state for this step also stays at loaded, but no error gets returned. For the first one, make sure to set up a listener or webhook so you can determine when that happens, and then you can cancel the order. For the second one, you need to check the async trace for the transaction after one hour. If it is still at loaded, then you can cancel the order.

Finally, if you know the popup was initiated (because of the previous state or an event was triggered, but async trace does not show that this popup state ever happens, you need to wait up to one hour from when the process started. If it is still not triggered, you can either cancel the order and retry with a new order number (because this may result in two transactions being processed) or you can make the order "pending" for up to 24 hours. After that point, the order will fail if it is not submitted.

finale

And the last possible state is finale.

This state begins when the system get a response from the 3DS provider and returns control to the merchant website. If the authentication was successful, the event listener of processed gets triggered. If there were connection issues, the system may return an error at this time.

When the async trace status for this state is initiated, the 3DS redirect has returned control to the merchant's finale page but there was an error. For example, this happens if the customer authenticates on the bank's 3DS page after more than one hour has elapsed. You should search for the transaction with the View transactions endpoint using a filter of the order ID for this transaction. If there is no result, wait at least 16 minutes after the initiatedAt timestamp for this state before checking again. After that, if there is still not result at that point, resend the transaction.

If the state is error, this indicates that there was an error between Nexio and the payment connection, or the customer may have failed authentication with their bank. Check the gatewayResponse and initialTransactionStatus in the async trace response to get more information for troubleshooting.

Otherwise, the state is responseSent. This means that Nexio returned a successful or declined payment response. Check the gatewayResponse and initialTransactionStatus in the async trace response to get more information about the successful or declined transaction.

A successful response also returns the payment response (see the next section).

Getting a response after 3DS authentication

The next part of the payment process is all about sending a payment request. For 3DS, the customer needs to also be authenticated with their bank.

After customers successfully authenticate, the system attempts to run the transaction.
Upon completion, the response will be returned as a message to the browser.

  1. {
      "amount": 34.25,
      "authCode": "035410",
      "card": {...},
      "currency": "USD",
      "data": {...},
      "gatewayResponse": {...},
      "id": "[paymentId]",
      "kountResponse": {...},
      "merchantId": "[merchant_id]",
      "token": {...},
      "transactionDate": "2023-01-15T13:19:39.329Z",
      "transactionStatus": "pending",
      "transactionType": "sale"
    }
    

    If you used a test card or information that does not work with the selected gateway connection, you get an error back to the webpage of 435 (or you can see it in the async trace or in a webhook). You may need to go back to the save card stage so you can process the transaction.