Search code examples
c#asp.net-coreblazor

Blazor Server app not redirecting to an external payment app url


<form @onsubmit="ProcessCheckout">
    <div>
        <h2>Checkout</h2>
        <button type="submit" class="btn btn-primary">Place Order</button>
        <a href="/cart" class="btn btn-secondary">Back to Shopping Bag</a>
    </div>
</form>

Best way to redirect to the authorizationUrl link in a new tab when the 'Place Order' button is clicked.

@code {
    private bool shouldRedirect;
    private string authorizationUrl;

    protected override async Task OnInitializedAsync()
    {
        await ProcessCheckout();
    }

    private async Task ProcessCheckout()
    {
        try
        {
            // Initialize checkout process
            var userId = await GetUserIdAsync();
            var netTotal = await _cartService.GetBasketNetTotalAsync(userId);

            var authState = await _authStateProvider.GetAuthenticationStateAsync();
            var user = authState.User;
            string strEmail = user.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;

            string token = _configuration["PayStackSettings:PayStackSecretKey"];
            var payStack = new PayStackApi(token);

            TransactionInitializeRequest request = new()
            {
                AmountInKobo = Convert.ToInt32(netTotal) * 100,
                Email = strEmail,
                Reference = Generate().ToString(),
                Currency = "GHS"
            };

            // Initiate transaction
            var response = payStack.Transactions.Initialize(request);
            if (response.Status)
            {
                authorizationUrl = response.Data.AuthorizationUrl;

                shouldRedirect = true;
                StateHasChanged(); // Trigger re-render
            }
        }
        catch (Exception ex)
        {            
            throw new Exception(ex.Message);
        }
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (shouldRedirect)
        {
            shouldRedirect = false; // Reset flag to prevent re-invoking
            await JS.InvokeVoidAsync("open", authorizationUrl, "_blank");
        }
    }

    private async Task<string> GetUserIdAsync()
    {
        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        return user.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
    }

    private static int Generate() => new Random((int)DateTime.Now.Ticks).Next(100000000, 999999999);
}

Hi guys! I'm trying to open the authorizationUrl in a new browser tab when the 'Place Order' button is clicked. My code is just refreshing the page though it hits the ProcessCheckout in debug mode. And please note that this is a Blazor Server application


Solution

  • I have resolved the issue by first adding @rendermode InteractiveServer to the page since I'm using a Blazor server application and by removing the OnInitializedAsync() method from the code so the final code becomes:

    @page "/checkout"
    @attribute [Authorize]
    @rendermode InteractiveServer
    @inject AuthenticationStateProvider _authStateProvider
    @inject ICartService _cartService
    @inject IUserService _userService
    @inject IConfiguration _configuration
    @inject IJSRuntime JS
    @inject NavigationManager _navigation
    
    <PageTitle>Checkout | LearnSpace</PageTitle>
    <SectionContent SectionName="page-header-title">Checkout</SectionContent>
    
    <div>
        <h2>Checkout</h2>
        <button class="btn btn-primary" @onclick="ProcessCheckout">Place Order</button>
        <a href="/cart" class="btn btn-secondary">Back to Shopping Bag</a>
    </div>
    
    @code {
        private bool shouldRedirect;
        private string authorizationUrl;
        private bool shouldOpenInNewTab = false;
    
        private async Task ProcessCheckout()
        {
            try
            {
                // Initialize checkout process
                var userId = await GetUserIdAsync();
                var netTotal = await _cartService.GetBasketNetTotalAsync(userId);
    
                var authState = await _authStateProvider.GetAuthenticationStateAsync();
                var user = authState.User;
                string strEmail = user.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;
    
                string token = _configuration["PayStackSettings:PayStackSecretKey"];
                var payStack = new PayStackApi(token);
    
                TransactionInitializeRequest request = new()
                {
                    AmountInKobo = Convert.ToInt32(netTotal) * 100,
                    Email = strEmail,
                    Reference = Generate().ToString(),
                    Currency = "GHS"
                };
    
                // Initiate transaction
                var response = payStack.Transactions.Initialize(request);
                if (response.Status)
                {
                    authorizationUrl = response.Data.AuthorizationUrl;
                    shouldOpenInNewTab = true;
                    StateHasChanged(); // Trigger re-render to ensure OnAfterRenderAsync runs
                }
            }
            catch (Exception ex)
            {            
                throw new Exception(ex.Message);
            }
        }
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (shouldOpenInNewTab && authorizationUrl != null)
            {
                shouldOpenInNewTab = false; // Reset the flag after the redirect
                await JS.InvokeVoidAsync("openInNewTab", authorizationUrl);
            }
        }
    
        private async Task<string> GetUserIdAsync()
        {
            var authState = await _authStateProvider.GetAuthenticationStateAsync();
            var user = authState.User;
            return user.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
        }
    
        private static int Generate() => new Random((int)DateTime.Now.Ticks).Next(100000000, 999999999);
    }
    

    So that's it.