Activities of "ageiter"

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") 

Unfortunately, no success in this matter. That didn't help and I don't see anything relevant via the dev-tools either.

But generally I have exceptions of this kind again and again... but the end user doesn't notice anything.

hi

Yes, We have added { StripeConsts.ParameterNames.PaymentRequestId, paymentRequest.Id.ToString()} in the StartAsync method just like yours.

Ok, then it should work.

Now you just have to assign the date (otherwise the method is useless). And I would not update the PaymentRequest, but solve this via the event (as before).

        var paymentUpdatedEto = ObjectMapper.Map<PaymentRequest, SubscriptionUpdatedEto>(paymentRequest);

        paymentUpdatedEto.PeriodEndDate = ConvertToDateTime((int)stripeEvent.Data.RawObject.current_period_end);

        await EventBus.PublishAsync(paymentUpdatedEto);

Have you tested this?

It will only work if you have also adapted the StartAsync method (PaymentRequestId in the metadata of the subscription). This is because the RawObject is a subscription and not a session, as is the case with HandleCheckoutSessionCompletedAsync, for example.

In addition, the date is not assigned. And finally, you make an update to the PaymentRequest, although you have not changed anything.

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();
            }})();
        ");
    }

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?

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

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}
        }
    };
}

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.&lt;InvokeActionMethodAsync&gt;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);
    }

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);
}

Thank you for searching for the relevant places. Unfortunately, none of this was new to me...

I have now made the try-catch block you requested around all PaymentRequestRepository.UpdateAsync calls. But I don't think that will do any help, as I already had it in the PalmaPaymentRequestAppService.

Does anything else happen between the PaymentRequestAppService and the StripePaymentGateway, other than the requests being passed through?

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

Showing 1 to 10 of 268 entries
Made with ❤️ on ABP v9.2.0-preview. Updated on January 20, 2025, 07:44