1

I have an application is ASP.net core and have integrated spreedly payment gateway for processing the payments. In my log files I can see that sometimes the payment controller executes twice. I generate an ID based on the time the request was received and the ID's are sometimes apart by 1 sec or at sometimes they are at the exact same time. This is resulting in charging the card twice only for few cases when this is happening. I cant seem to figure out what could be triggering this.

Following is the code that I am using

The user fills the application form and on the pay button click I am using this code to trigger spreedly

 $('#REG').click(function () {
            var options = {
                company_name: "abcd",
                sidebar_top_description: "Fees",
                sidebar_bottom_description: "Only Visa and Mastercard accepted",
                amount: "@string.Format("{0:c}",Convert.ToDecimal(Model.FeeOutstanding))"
            }
            document.getElementById('payment').value = 'App'
            SpreedlyExpress.init(environmentKey, options);
            SpreedlyExpress.openView();
            $('#spreedly-modal-overlay').css({ "position": "fixed", "z-index": "9999", "bottom": "0", "top": "0", "right": "0", "left": "0" });
        });

This opens the spreedly payment form as a popup where the user enters all the card information and hits the pay button. Which executes the payment controller

public async Task<IActionResult> Index(DynamicViewModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            if (TempData.ContainsKey("PaymentFlag") && !String.IsNullOrEmpty(TempData["PaymentFlag"].ToString()))
            {
                // Some code logic that calls few async methods
                //generate a id based on the time of current request
                "APP-" + DateTime.Now.ToString("yyyyMMddHmmss-") + model.UserID;

                // ... Other code here     
}

The id that I generate is logged and I can see that some times in the log file that it ran twice for a customer with the ID having either the exact same time or there was a 1 sec difference. I have tested the double click scenario and also have put in some code to prevent double clicks. But still I cant seem to understand why sometimes this happens. It is not frequent. Its like 1 case that happens in 100 payments.

I have an action attribute to handle the duplicate requests. After putting in this code it did stopped the number of duplicate requests but not completely. Still in few cases some how the controllers gets called twice.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NoDuplicateRequestAttribute : ActionFilterAttribute
{
    public int DelayRequest = 10;
    // The Error Message that will be displayed in case of 
    // excessive Requests
    public string ErrorMessage = "Excessive Request Attempts Detected.";

    // This will store the URL to Redirect errors to
    public string RedirectURL;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Store our HttpContext (for easier reference and code brevity)
        var request = filterContext.HttpContext.Request;
        // Store our HttpContext.Cache (for easier reference and code brevity)
        var cache = filterContext.HttpContext.RequestServices.GetService<IMemoryCache>();

        // Grab the IP Address from the originating Request (example)
        var originationInfo = request.HttpContext.Connection.RemoteIpAddress.ToString() ?? request.HttpContext.Features.Get<IHttpConnectionFeature>()?.RemoteIpAddress.ToString();

        // Append the User Agent
        originationInfo += request.Headers["User-Agent"].ToString();

        // Now we just need the target URL Information
        var targetInfo = request.HttpContext.Request.GetDisplayUrl() + request.QueryString;

        // Generate a hash for your strings (appends each of the bytes of
        // the value into a single hashed string
        var hashValue = string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(originationInfo + targetInfo)).Select(s => s.ToString("x2")));
        string cachedHash;
        // Checks if the hashed value is contained in the Cache (indicating a repeat request)
        if (cache.TryGetValue(hashValue,out cachedHash))
        {
            // Adds the Error Message to the Model and Redirect

        }
        else
        {
            // Adds an empty object to the cache using the hashValue
            // to a key (This sets the expiration that will determine
            // if the Request is valid or not)
            var opts = new MemoryCacheEntryOptions()
            {
                SlidingExpiration = TimeSpan.FromSeconds(DelayRequest)
            };
            cache.Set(hashValue,cachedHash,opts);
        }
        base.OnActionExecuting(filterContext);
    }
Cameron
  • 2,574
  • 22
  • 37
don great
  • 53
  • 1
  • 11
  • Your `Index` method on your controller is basically empty. We need more code there to diagnose an issue. I'd check the `Network` tab in your `Developer Tools` of your browser of choice to actually see if there are multiple requests getting fired off to your controller. Other than that, I'm not seeing anything crazy in the code you provided that would show that it's sending duplicate requests. – Cameron May 17 '17 at 15:35
  • In my case, the culprit was this line inside ``````. After I removed it duplicate requests had gone for good. – Burak Dobur Oct 02 '20 at 08:08
  • https://stackoverflow.com/a/68083749/1262673 – Project Mayhem Jun 22 '21 at 12:43

1 Answers1

0

This isn't an ASP.NET Core issue. I'm 99% certain there are in fact multiple requests coming from the client and ASP.NET Core is simply handling them as it is meant to.

An option for you would be to put a guid or other identifier on the page and send it with the request. In your Controller, check your cache or session to see if that identifier already exists. If it does, throw an exception or return Ok() or log the occurrence or whatever you want to do in that case but don't charge the card.

ssmith
  • 8,092
  • 6
  • 52
  • 93
  • I would generate a request ID rather than a page ID (or perhaps both), so you can see if the controller is invoked on two requests from the same page, two requests from two different pages OR indeed invoked twice because of a bug in one of the middlewares. – Yishai Galatzer May 17 '17 at 01:04
  • I already have an action attribute to handle this but still somehow this code is also not able to catch the requests in some cases. After putting in this code it actually brought down the number of duplicate requests drastically. I have updated my post to show the duplicate request code that i am using. – don great May 17 '17 at 15:25