using LiteCharms.Features.Api.Configuration; using LiteCharms.Features.Hasher; using LiteCharms.Features.MidrandBooks.AuthorBooks; using LiteCharms.Features.MidrandBooks.Orders; using LiteCharms.Features.MidrandBooks.Orders.Models; using LiteCharms.Features.MidrandBooks.Payments; using LiteCharms.Features.MidrandBooks.Payments.Models; namespace MidrandBookshop.Components.Pages; public partial class Checkout() { [Inject] public HashService HashService { get; set; } = default!; [Inject] public PaymentService PaymentService { get; set; } = default!; [Inject] public OrderService OrderService { get; set; } = default!; [Inject] public BooksService BooksService { get; set; } = default!; [Inject] public CartService CartService { get; set; } = default!; [Inject] public PayfastService PayfastService { get; set; } = default!; [Inject] public IOptions PayfastOptions { get; set; } = default!; [Inject] private AuthenticationStateProvider AuthStateProvider { get; set; } = default!; [Inject] public IJSRuntime JSRuntime { get; set; } = default!; [Inject] private HydrationService HydrationService { get; set; } = default!; [Inject] private CancellationToken CancellationToken { get; set; } = default!; private Cart ShoppingCart => CartService.ShoppingCart; private ClaimsPrincipal? User { get; set; } private bool IsProcessing { get; set; } private decimal ShippingCost = 0; private bool IsSameAddress = true; private Dictionary CheckoutPayload { get; set; } = []; protected override async Task OnInitializedAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); User = authState!.User; Navigation.LocationChanged += OnLocationChanged; CartService.OnCartChanged += CartService_OnCartChanged; await CartService.LoadCartFromStorageAsync(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender == false && HydrationService.CartHydrated == false) { await HydrationService.EnsureCustomerExistsAsync(CancellationToken); await HydrationService.RehydrateCartFromPendingOrderAsync(CancellationToken); CartService.NotifyStateChanged(); } } private async void CartService_OnCartChanged() => await InvokeAsync(StateHasChanged); private void OnLocationChanged(object? sender, LocationChangedEventArgs e) => StateHasChanged(); private async Task ChangeQuantity(CartItem item, int delta) { var peekQuantity = item.Quantity + delta; if (peekQuantity < 1) return; CartService.UpdateQuantity(item.Price!.Id, delta); await CartService.SaveCartToStorageAsync(); } private async Task RemoveFromCart(CartItem item) { CartService.RemoveOneItem(item.Price!.Id); await CartService.SaveCartToStorageAsync(); } private async Task PayNow(MouseEventArgs args) { if (IsProcessing) return; try { IsProcessing = true; StateHasChanged(); Result orderResult; var customerId = (long)ShoppingCart.CustomerId!; if (!ShoppingCart.OrderId.HasValue) { CreateOrder request = new(ShoppingCart.TotalAmount, null); orderResult = await OrderService.CreateOrderAsync(customerId, request, CancellationToken); ShoppingCart.OrderId = orderResult.Value; } List orderItems = []; var orderId = (long)ShoppingCart.OrderId; await OrderService.ClearOrderItemsAsync(orderId, CancellationToken); foreach (var item in ShoppingCart.Items) { var bookRequest = await BooksService.GetBookByProductIdAsync(item.Price!.Id, CancellationToken); if (bookRequest.IsSuccess) { var orderItem = new CreateOrderItem(bookRequest.Value.Id, item.Price.Id, item.Quantity); orderItems.Add(orderItem); } } var orderHash = HashService.HashEncodeLongId(orderId).Value; var paymentGen = await PaymentService.CreatePaymentAsync(ShoppingCart.TotalAmount, orderId, orderHash, CancellationToken); CreateLedgerEntry ledgerRequest = new() { OrderId = orderId, CustomerId = customerId, PaymentGatewayId = 1, // TODO: lookup value to match user selection PaymentGatewayReference = orderHash, PaymentId = paymentGen.Value, Status = LiteCharms.Features.LedgerStatuses.Sent, }; await PaymentService.WriteLedgerEntryAsync(ledgerRequest, CancellationToken); var addItemsResult = await OrderService.AddItemsToOrderAsync(orderId, [.. orderItems], CancellationToken); var hostAddress = Navigation.BaseUri.TrimEnd('/'); CheckoutPayload = new Dictionary { { "merchant_id", PayfastOptions.Value.MerchantId! }, { "merchant_key", PayfastOptions.Value.MerchantKey! }, { "return_url", $"{hostAddress}/payment-success" }, { "cancel_url", $"{hostAddress}/payment-failed" }, { "notify_url", "https://api.uat.midrandbooks.co.za/v1/payments/payfast/confirm" }, { "email_address", User?.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)!.Value! }, { "m_payment_id", orderHash }, { "amount", ShoppingCart.TotalAmount.ToString("F2", CultureInfo.InvariantCulture) }, { "item_name", "MidrandBooks Sale" }, }; var signature = PayfastService.GenerateSignature(CheckoutPayload!, PayfastOptions.Value.Passphrase).Value; CheckoutPayload.Add("signature", signature); StateHasChanged(); await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('payfastForm').submit();"); } catch { IsProcessing = false; StateHasChanged(); } } }