Open Closed

Concurrency exception when I use PaymentModule with own PaymentRequestGateway #8608


User avatar
0
ageiter created
  • ABP Framework version: v8.2.2
  • UI Type: Blazor Server
  • Database System: EF Core (SQL Server)
  • Tiered (for MVC) or Auth Server Separated (for Angular): no

We use the PaymentModule for payment processing via Stripe. Because we want to store various additional information (e.g. about the invoice) in the payment request, we have derived from the StripePaymentGateway and overwritten the CompleteAsync & HandleWebhookAsync methods, for example.

Now we always have concurrency exceptions because the PaymentModule in the PaymentRequestAppService probably also updates the PaymentRequest at the same time.

We have secured our methods with a semaphore so that the processing of our webhook events is synchronized and they cannot access the PaymentRequest at the same time. I have the code from your StripePaymentRequestGateway and we have overwritten all the corresponding methods there, so no relevant code is executed there.

But what happens in your PaymentRequestAppService is still a black box for us.

  1. could you provide me with the code? Preferably from the whole PaymentModule, if that is not possible, then at least from the PaymentRequestAppService.

  2. how should the problem be solved from your point of view? If you send events to the distributed event bus in the app service, then I won't be able to synchronize this with my own event processing, will I?

Thanks, Adrian


38 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    I will send you the new log file by e-mail in about 30 minutes...

    OK, I will check the logs.

  • User Avatar
    0
    ageiter created

    Update for those who also have this problem: It works with a unit of work with IsolationLevel.Serializable because a lock is then set at database level.

    using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true, isolationLevel: IsolationLevel.Serializable))
    {
        // get the entity 
    
        // update the entity
    
        await uow.CompleteAsync(cancellationToken);
    }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Thanks ageiter

  • User Avatar
    0
    ageiter created

    As I wrote yesterday in the mail to you @maliming, I have now changed my strategy and use the StripePaymentGateway almost without any changes on my part. Only at the end with CompleteAsync I do other things according to your code and retrieve e.g. the customer and billing information.

    Now I have noticed that there is an error in the HandleCustomerSubscriptionUpdateAsync method when calling the StripePaymentGateway. I assume it is because the external SubscriptionId is not yet saved in the PaymentRequest at the time of the call. You should actually be able to reproduce this error.

    The SubscriptionId from Stripe is saved in the HandleCheckoutSessionCompletedAsync() or CompleteAsync() method. However, since the update comes before, this ID is not yet present on the PaymentRequest and so this error occurs.

    Here is the log, enriched with my additional log messages from my wrapper class:

    2025-01-17 08:53:12.473 +00:00 [INF] PalmaPaymentRequestAppService.CreateAsync, Begin create payment request.   ThreadId='54'
    2025-01-17 08:53:12.511 +00:00 [INF] PalmaPaymentRequestAppService.CreateAsync, End create payment request.   ThreadId='54'
    2025-01-17 08:53:13.195 +00:00 [INF] PalmaPaymentRequestAppService.StartAsync, Begin start payment request for payment gateway 'stripe'.   ThreadId='86'
    2025-01-17 08:53:14.024 +00:00 [INF] PalmaPaymentRequestAppService.StartAsync, End start payment request for payment gateway 'stripe'.   ThreadId='54'
    2025-01-17 08:53:29.025 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: Begin handle webhook event 'checkout.session.completed' for payment gateway 'stripe'.   ThreadId='42'
    
    2025-01-17 08:53:29.069 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: Begin handle webhook event 'customer.subscription.updated' for payment gateway 'stripe'.   ThreadId='42'
    2025-01-17 08:53:29.130 +00:00 [ERR] ---------- RemoteServiceErrorInfo ----------
    {
      "code": null,
      "message": "There is no entity PaymentRequest with id = !",
      "details": null,
      "data": null,
      "validationErrors": null
    }
    
    2025-01-17 08:53:29.130 +00:00 [ERR] There is no such an entity given id. Entity type: Volo.Payment.Requests.PaymentRequest
    Volo.Abp.Domain.Entities.EntityNotFoundException: There is no such an entity given id. Entity type: Volo.Payment.Requests.PaymentRequest
       at Volo.Payment.EntityFrameworkCore.EfCorePaymentRequestRepository.GetBySubscriptionAsync(String externalSubscriptionId, CancellationToken cancellationToken)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Volo.Payment.Stripe.StripePaymentGateway.HandleCustomerSubscriptionUpdatedAsync(Event stripeEvent)
       at Volo.Payment.Stripe.StripePaymentGateway.HandleWebhookAsync(String payload, Dictionary`2 headers)
       at Volo.Payment.Requests.PaymentRequestAppService.HandleWebhookAsync(String paymentGateway, String payload, Dictionary`2 headers)
       at Palma.Payments.PalmaPaymentRequestAppService.HandleWebhookAsync(String paymentGateway, String payload, Dictionary`2 headers) in D:\a\1\s\Source\Palma\src\Palma.Application\Payments\PalmaPaymentRequestAppService.cs:line 60
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope)
       at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Volo.Payment.Requests.PaymentRequestController.HandleWebhookAsync(String paymentMethod, String payload, Dictionary`2 headers)
       at lambda_method6557(Closure, Object)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       
    2025-01-17 08:53:29.310 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: End handle webhook event 'checkout.session.completed' for payment gateway 'stripe'.   ThreadId='42'
    2025-01-17 08:53:29.485 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: Begin handle webhook event 'customer.subscription.created' for payment gateway 'stripe'.   ThreadId='42'
    2025-01-17 08:53:29.494 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: End handle webhook event 'customer.subscription.created' for payment gateway 'stripe'.   ThreadId='42'
    2025-01-17 08:53:31.813 +00:00 [INF] PalmaPaymentRequestAppService.CompleteAsync, Begin complete payment request for payment gateway 'stripe'.   ThreadId='54'
    2025-01-17 08:53:32.915 +00:00 [INF] PalmaPaymentRequestAppService.CompleteAsync, End complete payment request for payment gateway 'stripe'.   ThreadId='36'
    2025-01-17 08:53:43.938 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: Begin handle webhook event 'customer.subscription.updated' for payment gateway 'stripe'.   ThreadId='43'
    2025-01-17 08:53:43.987 +00:00 [INF] PalmaPaymentRequestAppService.HandleWebhookAsync: End handle webhook event 'customer.subscription.updated' for payment gateway 'stripe'.   ThreadId='43'
    

    This is the method from ABP StripePaymentGateway:

        protected virtual async Task HandleCustomerSubscriptionUpdatedAsync(Event stripeEvent)
        {
            var paymentRequest =
                await PaymentRequestRepository.GetBySubscriptionAsync(
                    (string)stripeEvent.Data.RawObject.id);
    
            var paymentUpdatedEto =
                ObjectMapper.Map<PaymentRequest, SubscriptionUpdatedEto>(paymentRequest);
    
            paymentUpdatedEto.PeriodEndDate =
                ConvertToDateTime((int)stripeEvent.Data.RawObject.current_period_end);
    
            await EventBus.PublishAsync(paymentUpdatedEto);
        }
    
  • User Avatar
    0
    ageiter created

    A possible bugfix / solution to this problem would be to also save the PaymentRequestId in the metadata of the subscription. Then you would not have to search for the PaymentRequest using the ExternalSubscriptionId, instead you would have the PaymentRequestId available directly.

    I have now changed this for me and it works.

    The adjustments in the StripePaymentGateway would be as follows:

    public virtual async Task<PaymentRequestStartResult> StartAsync(PaymentRequest paymentRequest, PaymentRequestStartInput input)
    {
    
        ...
    
        var options = new SessionCreateOptions
        {
            ...
    
            Metadata = new Dictionary<string, string>
            {
                { StripeConsts.ParameterNames.PaymentRequestId, paymentRequest.Id.ToString()},
            },
    
            // THIS IS THE CHANGE YOU SHOULD MAKE
            // Add metadata to the subscription
            SubscriptionData = new SessionSubscriptionDataOptions()
            {
                Metadata = new Dictionary<string, string>
                {
                    { StripeConsts.ParameterNames.PaymentRequestId, paymentRequest.Id.ToString() }
                }
            }
        };
        
        ...
    }
    

    HandleCustomerSubscriptionUpdatedAsync would be as follows:

            protected override async Task HandleCustomerSubscriptionUpdatedAsync(Event stripeEvent)
            {
                var paymentRequestId = Guid.Parse(stripeEvent.Data.RawObject.metadata[StripeConsts.ParameterNames.PaymentRequestId]?.ToString());
                var paymentRequest = await PaymentRequestRepository.GetAsync(paymentRequestId);
    
                var paymentUpdatedEto =
                    ObjectMapper.Map<PaymentRequest, SubscriptionUpdatedEto>(paymentRequest);
    
                paymentUpdatedEto.PeriodEndDate =
                    ConvertToDateTime((int)stripeEvent.Data.RawObject.current_period_end);
    
                await EventBus.PublishAsync(paymentUpdatedEto);
            }
    

    Additional improvements in the StripePaymentGateway:

    • Also adjusts method HandleCustomerSubscriptionDeletedAsync described above
    • Always uses the constant StripeConsts.ParameterNames.PaymentRequestId (not Guid.Parse(session.Metadata["PaymentRequestId"]))

    Here is the whole StartAsync method:

    public virtual async Task<PaymentRequestStartResult> StartAsync(PaymentRequest paymentRequest, PaymentRequestStartInput input)
    {
        var purchaseParameters = PurchaseParameterListGenerator.GetExtraParameterConfiguration(paymentRequest);
    
        var currency = paymentRequest.Currency.IsNullOrEmpty() ? purchaseParameters.Currency : paymentRequest.Currency;
    
        var lineItems = new List<SessionLineItemOptions>();
    
        foreach (var product in paymentRequest.Products)
        {
            var lineItem = new SessionLineItemOptions
            {
                Quantity = product.Count,
            };
    
            if (product.PaymentType == PaymentType.Subscription)
            {
                var gatewayPlan = await PlanRepository.GetGatewayPlanAsync(product.PlanId.Value, StripeConsts.GatewayName);
                lineItem.Price = gatewayPlan.ExternalId;
            }
    
            if (product.PaymentType == PaymentType.OneTime)
            {
                lineItem.PriceData = new SessionLineItemPriceDataOptions
                {
                    UnitAmountDecimal = Convert.ToDecimal(product.UnitPrice) * 100,
                    Currency = currency,
                    ProductData = new SessionLineItemPriceDataProductDataOptions
                    {
                        Name = product.Name,
                        Metadata = new Dictionary<string, string>
                            {
                                { "ProductCode", product.Code }
                            }
                    }
                };
            }
    
            lineItems.Add(lineItem);
        }
    
        var options = new SessionCreateOptions
        {
            Locale = purchaseParameters.Locale,
            PaymentMethodTypes = purchaseParameters.PaymentMethodTypes,
            LineItems = lineItems,
    
            Mode = modeMapping[paymentRequest.Products.First().PaymentType],
    
            SuccessUrl = input.ReturnUrl,
            CancelUrl = input.CancelUrl,
    
            Metadata = new Dictionary<string, string>
            {
                { StripeConsts.ParameterNames.PaymentRequestId, paymentRequest.Id.ToString()},
            },
    
            // Add metadata to the subscription
            SubscriptionData = new SessionSubscriptionDataOptions()
            {
                Metadata = new Dictionary<string, string>
                {
                    { StripeConsts.ParameterNames.PaymentRequestId, paymentRequest.Id.ToString() }
                }
            }
        };
    
        var sessionService = new SessionService();
        var session = await sessionService.CreateAsync(options);
    
        return new PaymentRequestStartResult
        {
            CheckoutLink = "https://js.stripe.com/v3/",
            ExtraProperties =
            {
                { StripeConsts.ParameterNames.PublishableKey, StripeOptions.PublishableKey},
                { StripeConsts.ParameterNames.SessionId, session.Id}
            }
        };
    }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Many thanks for your solution. 👍

    I will check our payment module

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Your ticket has been refunded.

  • User Avatar
    0
    ageiter created

    Thank you very much. Could you let me know in which version this has been implemented?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Maybe 9.2

  • User Avatar
    0
    ageiter created

    One last question about the payment process. You have probably also seen in my logs (which I sent you by e-mail) that the following error message always appears at the beginning of a payment request (after the StartAsync):

    2025-01-16 19:55:43.645 +00:00 [INF] PalmaPaymentRequestAppService.StartAsync, Begin start payment request for payment gateway 'stripe'.   ThreadId='37'
    2025-01-16 19:55:44.898 +00:00 [INF] PalmaPaymentRequestAppService.StartAsync, End start payment request for payment gateway 'stripe'.   ThreadId='38'
    2025-01-16 19:55:45.102 +00:00 [WRN] The operation was canceled.
    System.OperationCanceledException: The operation was canceled.
       at System.Threading.CancellationToken.ThrowOperationCanceledException()
       at Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache.GetAsync(String key, CancellationToken token)
       at Volo.Abp.Caching.DistributedCache`2.GetAsync(TCacheKey key, Nullable`1 hideErrors, Boolean considerUow, CancellationToken token)
    2025-01-16 19:55:45.173 +00:00 [ERR] Error when dispatching 'OnDisconnectedAsync' on hub.
    System.Threading.Tasks.TaskCanceledException: A task was canceled.
       at Volo.Abp.Threading.SemaphoreSlimExtensions.LockAsync(SemaphoreSlim semaphoreSlim, CancellationToken cancellationToken)
       at Volo.Abp.Caching.DistributedCache`2.GetOrAddAsync(TCacheKey key, Func`1 factory, Func`1 optionsFactory, Nullable`1 hideErrors, Boolean considerUow, CancellationToken token)
       at Volo.Abp.Identity.IdentityDynamicClaimsPrincipalContributorCache.GetAsync(Guid userId, Nullable`1 tenantId)
       at Volo.Abp.Identity.IdentityDynamicClaimsPrincipalContributor.ContributeAsync(AbpClaimsPrincipalContributorContext context)
       at Volo.Abp.Security.Claims.AbpClaimsPrincipalFactory.InternalCreateAsync(AbpClaimsPrincipalFactoryOptions options, ClaimsPrincipal existsClaimsPrincipal, Boolean isDynamic)
       at Volo.Abp.Security.Claims.AbpClaimsPrincipalFactory.CreateDynamicAsync(ClaimsPrincipal existsClaimsPrincipal)
       at Volo.Abp.AspNetCore.SignalR.Authentication.AbpAuthenticationHubFilter.HandleDynamicClaimsPrincipalAsync(ClaimsPrincipal claimsPrincipal, IServiceProvider serviceProvider, HubCallerContext hubCallerContext, Boolean skipCheckDynamicClaimsInterval)
       at Volo.Abp.AspNetCore.SignalR.Authentication.AbpAuthenticationHubFilter.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnDisconnectedAsync(HubConnectionContext connection, Exception exception)
       at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnDisconnectedAsync(HubConnectionContext connection, Exception exception)
       at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.HubOnDisconnectedAsync(HubConnectionContext connection, Exception exception)
    2025-01-16 19:55:45.203 +00:00 [ERR] Failed disposing connection 3FgE5QW7nXvr8qTHdKaf_A.
    System.Threading.Tasks.TaskCanceledException: A task was canceled.
       at Volo.Abp.Threading.SemaphoreSlimExtensions.LockAsync(SemaphoreSlim semaphoreSlim, CancellationToken cancellationToken)
       at Volo.Abp.Caching.DistributedCache`2.GetOrAddAsync(TCacheKey key, Func`1 factory, Func`1 optionsFactory, Nullable`1 hideErrors, Boolean considerUow, CancellationToken token)
       at Volo.Abp.Identity.IdentityDynamicClaimsPrincipalContributorCache.GetAsync(Guid userId, Nullable`1 tenantId)
       at Volo.Abp.Identity.IdentityDynamicClaimsPrincipalContributor.ContributeAsync(AbpClaimsPrincipalContributorContext context)
       at Volo.Abp.Security.Claims.AbpClaimsPrincipalFactory.InternalCreateAsync(AbpClaimsPrincipalFactoryOptions options, ClaimsPrincipal existsClaimsPrincipal, Boolean isDynamic)
       at Volo.Abp.Security.Claims.AbpClaimsPrincipalFactory.CreateDynamicAsync(ClaimsPrincipal existsClaimsPrincipal)
       at Volo.Abp.AspNetCore.SignalR.Authentication.AbpAuthenticationHubFilter.HandleDynamicClaimsPrincipalAsync(ClaimsPrincipal claimsPrincipal, IServiceProvider serviceProvider, HubCallerContext hubCallerContext, Boolean skipCheckDynamicClaimsInterval)
       at Volo.Abp.AspNetCore.SignalR.Authentication.AbpAuthenticationHubFilter.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.HubFilterFactory.OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func`3 next)
       at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnDisconnectedAsync(HubConnectionContext connection, Exception exception)
       at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnDisconnectedAsync(HubConnectionContext connection, Exception exception)
       at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.HubOnDisconnectedAsync(HubConnectionContext connection, Exception exception)
       at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)
       at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.OnConnectedAsync(ConnectionContext connection)
       at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.OnConnectedAsync(ConnectionContext connection)
       at Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext.ExecuteApplication(ConnectionDelegate connectionDelegate)
       at Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext.WaitOnTasks(Task applicationTask, Task transportTask, Boolean closeGracefully)
       at Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext.DisposeAsync(Boolean closeGracefully)
       at Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager.DisposeAndRemoveAsync(HttpConnectionContext connection, Boolean closeGracefully, HttpConnectionStopStatus status)
    

    Why is this the case? How can I correct this?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    A task was canceled.

    This is usually because the http(web hook) request was canceled.

  • User Avatar
    0
    ageiter created

    It just irritates me because it always happens (every time) after the StartAsync. So when redirecting to the PaymentGateway. At a time when no WebHooks are being received.

    Maybe it has something to do with the way I submit the request...

        private async Task RedirectToPaymentGateway(Guid paymentRequestId)
        {
            var tokens = Antiforgery.GetAndStoreTokens(HttpContextAccessor.HttpContext);
            var requestToken = tokens.RequestToken;
    
            var uri = $"/Payment/GatewaySelection?paymentRequestId={paymentRequestId}";
            
            await JSRuntime.InvokeVoidAsync("eval", $@"
                (function() {{
                    var form = document.getElementById('PaymentGatewaySelectionForm');
                    
                    // Check if the antiforgery token is present in the form, if the token is not present, create a new hidden input field for the token
                    var tokenInput = form.querySelector('input[name=__RequestVerificationToken]');
                    if (!tokenInput) {{
                        tokenInput = document.createElement('input');
                        tokenInput.type = 'hidden';
                        tokenInput.name = '__RequestVerificationToken';
                        form.appendChild(tokenInput);
                    }}
                    
                    // Set the value of the token
                    tokenInput.value = '{requestToken}';
    
                    // Update the form's action with the new URI and submit the form
                    form.action = '{uri}';
                    form.submit();
                }})();
            ");
        }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you check your question in Chrome dev-tool?

    You can consider moving the js code to a function(PostPaymentGatewaySelectionForm), then Invoke this function in blazor.

    await JSRuntime.InvokeVoidAsync("PostPaymentGatewaySelectionForm")
    

    By the way, can you share the cs files you have overwritten? I will check and merge into the payment module.

    Thanks.

Made with ❤️ on ABP v9.2.0-preview. Updated on January 16, 2025, 11:47