DEV Community

Cover image for Solving Complex Business Logic for User Registration and Checkout in C# .NET
Ibrahim Suleiman
Ibrahim Suleiman

Posted on

Solving Complex Business Logic for User Registration and Checkout in C# .NET

Introduction

Hello, dev.to community! 👋

I'm excited to share my first blog post with you. Well, to be honest I was tasked with creating a blog as part of the requirements for the HNG Internship program I joinded recently.

HNG Internship is a fast-paced bootcamp for learning digital skills. It's focused on advanced learners and those with some pre-knowledge, and it gets people into shape for job offers.

Expand your horizons by joining the HNG Premium Network to connect with top techies, grow your career, and collaborate with others. And that is exactly what I plan on doing!

As a backend developer specializing in C# and .NET, I've tackled numerous challenges in my projects. Being in an active community such as the one provided by the HNG INternship, one can easily network with fellow techies and find solutions to tasks that may seem daunting.

One of the most complex problems I encountered recently was ensuring that all users (whether unregistered, registered with minimal details, or registered with full details) had the necessary information filled out before proceeding to checkout in an ecommerce application. In this post, I'll walk you through the steps I took to solve this problem, from understanding the requirements to implementing the solution and testing it.

The Requirements

The goal was to ensure that users couldn't proceed to checkout without providing the following details:

  • First Name
  • Last Name
  • Phone Number
  • Payment Details
  • Billing Address
  • Shipping Address

I had to handle different types of users:

  • Unregistered Users: Users who haven't created an account.
  • Registered Users with Minimal Details: Users who registered with just an email and password.
  • Registered Users with Full Details: Users who provided complete information during registration.

Designing the Data Models

I used Entity Framework Core code-first approach to design the data models for users, addresses, and payment details. The user model leveraged the IdentityUser to take advantage of the features provided by Microsoft such as automatic hashing and salting of passwords.

public class ApplicationUser : IdentityUser
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address BillingAddress { get; set; }
    public Address ShippingAddress { get; set; }
}

public class Address
{
    public Guid Id { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
}

public class PaymentDetails
{
    public Guid Id { get; set; }
    public string CardNumber { get; set; }
    public string CardHolderName { get; set; }
    public DateTime ExpiryDate { get; set; }
    public string CVV { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Creating the Services

Next, I created services to manage user data and validate that all required fields were filled out before checkout.

public class UserService
{
    private readonly UserManager<ApplicationUser> _userManager;

    public UserService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task<bool> ValidateUserDetailsAsync(ApplicationUser user)
    {
        // Logic to verify that all fields were correctly filled out
    }
}

public class CheckoutService
{
    private readonly UserService _userService;
    private readonly OrderService _orderService;

    public CheckoutService(UserService userService, OrderService orderService)
    {
        _userService = userService;
        _orderService = orderService;
    }

    public async Task<CheckoutResult> CheckoutAsync(ApplicationUser user, Order order)
    {
        if (!await _userService.ValidateUserDetailsAsync(user))
        {
            return new CheckoutResult { Success = false, Message = "User details are incomplete" };
        }

        // Proceed with order processing
        await _orderService.ProcessOrderAsync(order);

        return new CheckoutResult { Success = true, Message = "Checkout successful" };
    }
}

public class CheckoutResult
{
    public bool Success { get; set; }
    public string Message { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Implementing the API Endpoints

With the services in place, I created API endpoints for user registration and checkout. This was pretty easy to implement because the services were doing most of the heavylifting.

[ApiController]
[Route("api/[controller]")]
public class CheckoutController : ControllerBase
{
    private readonly CheckoutService _checkoutService;

    public CheckoutController(CheckoutService checkoutService)
    {
        _checkoutService = checkoutService;
    }

    [HttpPost("checkout")]
    public async Task<IActionResult> Checkout(CheckoutDto checkoutDto)
    {
        var user = await _userService.GetUserAsync(checkoutDto.UserId);
        var order = await _orderService.GetOrderAsync(checkoutDto.OrderId);

        var result = await _checkoutService.CheckoutAsync(user, order);

        if (!result.Success)
        {
            return BadRequest(result.Message);
        }

        return Ok(result.Message);
    }
}
Enter fullscreen mode Exit fullscreen mode

Testing and Validation

To ensure everything worked correctly, I wrote unit tests for the business logic in the services and integration tests for the API endpoints.

public class UserServiceTests
{
    [Fact]
    public async Task ValidateUserDetailsAsync_ShouldReturnFalse_WhenDetailsAreIncomplete()
    {
        var userService = new UserService();
        var user = new User { FirstName = "John", LastName = "Doe" }; // Incomplete details

        var result = await userService.ValidateUserDetailsAsync(user);

        Assert.False(result);
    }

    [Fact]
    public async Task ValidateUserDetailsAsync_ShouldReturnTrue_WhenDetailsAreComplete()
    {
        var userService = new UserService();
        var user = new User
        {
            FirstName = "John",
            LastName = "Doe",
            PhoneNumber = "1234567890",
            BillingAddress = new Address { /* complete address */ },
            ShippingAddress = new Address { /* complete address */ }
        };

        var result = await userService.ValidateUserDetailsAsync(user);

        Assert.True(result);
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Implementing this solution was a significant milestone in my journey as a backend developer. By breaking down the problem into manageable steps, I was able to handle complex user registration and checkout processes efficiently.

I hope this post helps you in your own backend development journey. Feel free to reach out if you have any questions or comments!

Top comments (0)