Open Closed

Payments Module UI - Blazor Server #2251


User avatar
0
marketbus created

There is currently no documentation / code examples for using the payments module to process a payment from end to end using blazor server. Could you provide an example? PaymentWebOptions for example, is not included in the default nugets for blazor server.

I am able to create a payment using :

var paymentRequest = await _paymentRequestAppService.CreateAsync(new PaymentRequestCreateDto()
{
    Products = new List<PaymentRequestProductCreateDto>()
    {
        new PaymentRequestProductCreateDto
        {
            Code = "Product_01",
            Name = "LEGO Super Mario",
            Count = 2,
            UnitPrice = 60,
            TotalPrice = 200
        }
    }
});

However, I am not able to process/complete a payment. I tried passing paymentRequest.Id to _paymentRequestAppService.CompleteAsync(new CompletePaymentRequestDto(){ }) But I am getting a stripe exception stating that the sessionId is missing.

Also, when testing from within stripe, the default yourdomain.com/payment/stripe/webhook endpoint do not work. Events sent to this endpoint return an exception.


17 Answer(s)
  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Hi @marketbus,

    But I am getting a stripe exception stating that the sessionId is missing.

    Can you please share what exception you got?


    After a payment request was created, you need to redirect to/Payment/GatewaySelection?paymentRequestId=THE_ID_OF_PAYMENTREQUEST endpoint. If that endpoint doesn't exist in your application make sure your project has Volo.Payment.HttpApi and Volo.Payment.Web dependencies.

    Also, when testing from within stripe, the default yourdomain.com/payment/stripe/webhook endpoint do not work. Events sent to this endpoint return an exception.

    Volo.Payment.HttpApi includes webhook endpoints. If you add that dependency properly, the endpoint will exist in your application. Also sending a test webhook will always return an error because the application can't find any entry with a randomly generated id sent by test request.

    Volo.Payment.Web includes web components and pages such as 'GatewaySelection', 'PrePayment', 'PostPayment' etc. If you add that dependency and configure it properly, your flow should work without an error.

    If the project already has those dependencies and the exception is different than 404 not found error, please share your logs and which dependencies your project has.

  • User Avatar
    0
    marketbus created

    This is a Blazor Server-Side Application. I assumed that Volo.Payment.Web was for an MVC application.

    I used ABP Suite to create my project. Although the Volo.Payment.HttpApi nuget was added to the [ProjectName].HttpApi project. Volo.Payment.Web has to be manually added to the solution.

    Even then, doing a simple test in the browser by going to the url /Payment/GatewaySelection?paymentRequestId=12345 returns an autofac exception. If you can upload a sample ABP Suite Blazor Server-Side project to Github with the payment Gateway url working that would be great.

    ComponentNotRegisteredException: The requested service 'Volo.Payment.Pages.Payment.GatewaySelectionModel' has not been registered.

    Volo.Payment.HttpApi includes webhook endpoints. If you add that dependency properly, the endpoint will exist in your application. Also sending a test webhook will always return an error because the application can't find any entry with a randomly generated id sent by test request.

    When testing the webhook through stripe It's returning a 404, not a 400.

    Can you please share what exception you got?

    System.Collections.Generic.KeyNotFoundException: The given key 'SessionId' was not present in the dictionary.

    It's looking for me to do:

    await _paymentRequestAppService.CompleteAsync(new CompletePaymentRequestDto() { Id = paymentRequest.Id, GateWay = "stripe", ExtraProperties = { { "SessionId", ????? } } });

    But since I am not able to suppy an SessionId it's throwing that exception.

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    You're right, Volo.Payment.Web is a MVC implementation of payment but that's what we have right now. Payment module has only Web implementation and you need to add that dependency to use PaymentGateway page. You can still configure the return URL and your users won't see those pages.

    Even in an angular application, payment operations are running on server pages (MVC).


    Payment gateways can't be provided by an API, it's implemented something like that:

      Gateways = _paymentWebOptions.Value.Gateways
                .Where(x => gatewaysDtos.Any(a => a.Name == x.Key))
                .Select(x => x.Value)
                .ToList();
    

    If you still want to implement for blazor, you'll need to implement for each payment provider, currently, we have the following implementations:

    • Volo.Payment.Iyzico.Web
    • Volo.Payment.PayPal.Web
    • Volo.Payment.Payu.Web
    • Volo.Payment.Stripe.Web
    • Volo.Payment.TwoCheckout.Web
  • User Avatar
    0
    marketbus created

    Actually, I do not really care about the other Gateways. I intend on only using Stripe. Can you provide an example of how this will be done to integrate it with ABP?

    Otherwise, I will have to write out my own Stripe integration.

    At a high level, It would seem that I would use IPaymentRequestAppService to create a PaymentRequest. Map the PaymentRequest to a Stripe LineItems Object. Create a session using the Session Service. Then pass that SessionId to IPaymentRequestAppService's CompleteAsync method.

    One of the main reasons why I signed up for the Commercial version of ABP, is actually because of the payments module. I am a sole developer, it's not really worth it to upgrade to one of your higher plans that include the complete source code. The payment's module without proper Blazor support seems incomplete.

  • User Avatar
    0
    marketbus created

    I basically followed the method that I mentioned above, and everything works, however, when I try to complete the transaction by calling CompleteAsync on the PaymentRequestAppService it throws an exception

    "System.Collections.Generic.KeyNotFoundException: The given key 'PaymentRequestId' was not present in the dictionary."

    I am able to verify that the transaction has been completed in the stripe dashboard, however, I would like it to be updated on the admin page as well.

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Hi @marketbus

    Actually, I do not really care about the other Gateways. I intend on only using Stripe. Can you provide an example of how this will be done to integrate it with ABP?

    Otherwise, I will have to write out my own Stripe integration.

    At a high level, It would seem that I would use IPaymentRequestAppService to create a PaymentRequest. Map the PaymentRequest to a Stripe LineItems Object. Create a session using the Session Service. Then pass that SessionId to IPaymentRequestAppService's CompleteAsync method.

    One of the main reasons why I signed up for the Commercial version of ABP, is actually because of the payments module. I am a sole developer, it's not really worth it to upgrade to one of your higher plans that include the complete source code. The payment's module without proper Blazor support seems incomplete.

    Main point of Payment module is supporting more than one provider at the same time and let use to choose payment method. It's all about abstraction and yes you have to create a PaymentRequest and then that PaymentRequest must be paid.

    I'm sorry about if the module doesn't meet your requirements. We'll work on according your feedbacks, thanks.

    I basically followed the method that I mentioned above, and everything works, however, when I try to complete the transaction by calling CompleteAsync on the PaymentRequestAppService it throws an exception

    "System.Collections.Generic.KeyNotFoundException: The given key 'PaymentRequestId' was not present in the dictionary."

    I am able to verify that the transaction has been completed in the stripe dashboard, however, I would like it to be updated on the admin page as well.

    If you please share code blocks where IPaymentRequestAppService is used. So I can understand the exception and find a solution.

  • User Avatar
    0
    marketbus created

    This is how I am using it:

    await _paymentRequestAppService.CompleteAsync(new CompletePaymentRequestDto()
    {
        Id = completedPayment.PaymentRequestId, GateWay = "stripe", 
        ExtraProperties = { { "SessionId", completedPayment.Id } }
    });
    

    the PaymentRequestId is what is returned from

    var paymentRequest = await _paymentRequestAppService.CreateAsync(new PaymentRequestCreateDto()
    {
        Products = paymentRequestDto.Products
    });
    
    

    Also, I updated to the latest stripe nuget as it supports server-side redirects. When you create a stripe session it returns a URL in the response that you can redirect to. I thought this would be the simpler/better approach rather than integrating with stripe.js and having to do Javascript Interop.

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    So at that exception:

    "System.Collections.Generic.KeyNotFoundException: The given key 'PaymentRequestId' was not present in the dictionary."
    

    Can you see a file path and line number? I need entire exception stacktrace right now because your usage looks it is right

  • User Avatar
    0
    marketbus created

    This is the full stack trace:

    ystem.Collections.Generic.KeyNotFoundException: The given key 'PaymentRequestId' was not present in the dictionary.
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at Volo.Payment.Stripe.StripePaymentGateway.IsValid(PaymentRequest paymentRequest, Dictionary`2 properties)
       at Volo.Payment.Requests.PaymentRequestAppService.CompleteAsync(CompletePaymentRequestDto input)
       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.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, 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.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.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at ReferMe.Payments.StripePaymentService.CompletePayment(SessionDto completedPayment) in D:\_dev\ReferMe\src\ReferMe.Application\Payments\StripePaymentService.cs:line 62
    
  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    I think problem occurs following check from source code of Payment Module:

    public class StripePaymentGateway : IPaymentGateway, ITransientDependency
    {
        public bool IsValid(PaymentRequest paymentRequest, Dictionary<string, object> properties)
        {
            var sessionId = properties["SessionId"]?.ToString();
            if (sessionId.IsNullOrWhiteSpace())
            {
                throw new Exception("Empty SessionId.");
            }
    
            var sessionService = new SessionService();
            var session = sessionService.Get(sessionId);
    
            if (!session.PaymentStatus.Equals("paid", StringComparison.InvariantCulture))
            {
                throw new Exception("Session not paid.");
            }
    
            return session.Metadata["PaymentRequestId"] == paymentRequest.Id.ToString();
        }
    }
    
    

    Can you check if session.Metadata["PaymentRequestId"] is set or not?

  • User Avatar
    0
    marketbus created

    Thanks that helped. It works now. I am now able to confirm a completed payment in the admin page.

    When I create the SessionCreateOptions object, I add the PaymentRequestId to the Metadata dictionary.

    Quick question, are the webhooks going to work? Or do I need to implement that separately? I believe that they should be included in the Volo.Payment.Stripe.HttpApi nuget. However, when I test it from Stripe, I am getting a 404.

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Yes, webhooks are handled in HttpApi package. You need to configure WebHookSecret in StripeOptions for the project which includes that HttpApi package.

    If you complete this step for configuring webhooks, it'll update the status of PaymentRequest entities according to webhook feeds.

  • User Avatar
    0
    christophe.baille created

    After a payment request was created, you need to redirect to/Payment/GatewaySelection?paymentRequestId=THE_ID_OF_PAYMENTREQUEST endpoint. If that endpoint doesn't exist in your application make sure your project has Volo.Payment.HttpApi and Volo.Payment.Web dependencies.

    --> Could you share the code on how you redirect to /GatewaySelection?paymentRequestId=THE_ID_OF_PAYMENTREQUEST on Blazor please? I made it work on Mvc host app, but I am new to Blazor and do not find out on which way I should do it... I tried httpClient.PostAsJsonAsync and navManager.NavigateTo but no luck so far.

    Sorry to interrupt you and thanks in advance for your help :)

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Blazor Server works hybrid. it includes MVC endpoints too. If you redirect to /GatewaySelection?paymentRequestId=THE_ID_OF_PAYMENTREQUEST after creating a PaymentRequest, an MVC page will handle and a page will be shown for payment provider selection.

    Before doing that, make sure your project has reference to Volo.Payment.Web and Volo.Payment.Stripe.Web. If you include Web layers in a Blazor project, it will switch to razor pages when required.

    Please use forceLoad while redirecting with NavigationManager:

    NavigationManager.NavigateTo("/GatewaySelection?paymentRequestId=X", forceLoad: true);
    
  • User Avatar
    0
    christophe.baille created

    I added forceLoad: true on my class ViewDetails.razor.cs, so now it redirect to the url

    https://localhost:44307/GatewaySelection?paymentRequestId=42912c2c-8956-2bd2-e0b4-3a015ec78851

    which doesn't exist as I need to add both references Volo.Payment.Web and Volo.Payment.Stripe.Web on my Blazor project as you said. I then got a new error about

    There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'. C:\Program Files\dotnet\sdk\6.0.101\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets

    After adding this references, I got an error, after some research on internet, I tried to replace

    <PackageReference Include="Volo.Abp.Autofac.WebAssembly" Version="5.0.0" /> by <PackageReference Include="Volo.Abp.Autofac.Web" Version="5.0.0" />

    and add

    <UseBlazorWebAssembly>true</UseBlazorWebAssembly>

    I can then run the application but Blazor application do not show anymore... I keep looking further as I migrate from ABP 4 to 5, I might have other things missing...

    I understand that I should not ask more support on here as it is not my ticket, but I will ask, as Marketbus asked, if it is possible to have a sample solution to see on how it is implemented. I am far to be an expert on Blazor so it is pretty hard to implement this feature with only the MVC doc...

    Thanks

  • User Avatar
    1
    enisn created
    Support Team .NET Developer

    Hi @christophe.baille

    You can't use Volo.Payment.Web and Volo.Payment.Stripe.Web packages inside your Blazor WebAssembly. Web layers are only compatible with Blazor Server.

    Thank you for your answer, I'll plan to publish an article about making payments in Blazor UI.

  • User Avatar
    1
    marketbus created

    Hi @christophe.baille

    You can't use Volo.Payment.Web and Volo.Payment.Stripe.Web packages inside your Blazor WebAssembly. Web layers are only compatible with Blazor Server.

    Thank you for your answer, I'll plan to publish an article about making payments in Blazor UI.

    I believe this would be very helpful. Thanks. If we had access to the source code, of course, this wouldn't be necessary. But as things stand, documentation is key.

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11