Skip to content

feat: Enhance PaymentRequest with rich domain model , value objects and unit tests - #4

Open
hamedzsmm wants to merge 1 commit into
mainfrom
feat/create-domain-valueobjects-tests
Open

feat: Enhance PaymentRequest with rich domain model , value objects and unit tests#4
hamedzsmm wants to merge 1 commit into
mainfrom
feat/create-domain-valueobjects-tests

Conversation

@hamedzsmm

Copy link
Copy Markdown
Collaborator
  • Added PaymentRequest and ValueObjects
  • Implemented validation logic for fields like MobileNumber, Email, and CallbackUrl.
  • Added behaviors to PaymentRequest, including:
    • SetCard, SetPayer, UpdatePaymentStatus, SetGatewayNote and other methods.
  • Introduced static factory method Create for controlled instantiation.
  • Developed comprehensive unit tests for PaymentRequest and their value objects
  • Ensured encapsulation and adherence to rich domain model principles.

@imhmdi
imhmdi requested a review from CopilotMay 16, 2025 08:36

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a rich domain model around payment requests with encapsulated value objects, enforced validation, and accompanying unit tests.

  • Defines value objects (Money, CardNumber, CVV, ExpiryDate, Payer, Card, BaseCurrency) with field validation.
  • Implements PaymentRequest entity behaviors (factory instantiation, setters, status updates) and validation.
  • Adds unit tests and a masking service for payment card fields.

Reviewed Changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.

Show a summary per file
FileDescription
src/Tests/Honamic.PayMaster.Abstractions.UnitTest/CardNumberTest.csAdds unit tests for CardNumber validation
src/Tests/Honamic.PayMaster.Abstractions.UnitTest/CardMaskingServiceTest.csAdds unit tests for CardMaskingService
src/Tests/Honamic.PayMaster.Abstractions.UnitTest/CVVTest.csAdds unit tests for CVV validation
src/PayMaster.slnRegisters the new unit test project and solution folder
src/Abstractions/PaymentProviders/ValueObjects/Payer.csIntroduces Payer value object
src/Abstractions/PaymentProviders/ValueObjects/Money.csIntroduces Money value object with arithmetic methods
src/Abstractions/PaymentProviders/ValueObjects/ExpiryDate.csIntroduces ExpiryDate value object with expiry checks
src/Abstractions/PaymentProviders/ValueObjects/CardNumber.csIntroduces CardNumber value object with length validation
src/Abstractions/PaymentProviders/ValueObjects/Card.csIntroduces Card aggregate value object
src/Abstractions/PaymentProviders/ValueObjects/CVV.csIntroduces CVV value object with length validation
src/Abstractions/PaymentProviders/ValueObjects/BaseCurrency.csDefines supported currencies and symbols
src/Abstractions/PaymentProviders/Services/CardMaskingService.csImplements card number masking logic
src/Abstractions/PaymentProviders/Exceptions/PaymentNotFoundException.csAdds PaymentNotFoundException
src/Abstractions/PaymentProviders/Exceptions/InvalidExpiryDateException.csAdds InvalidExpiryDateException
src/Abstractions/PaymentProviders/Exceptions/InvalidCurrencyException.csAdds InvalidCurrencyException
src/Abstractions/PaymentProviders/Exceptions/InvalidCardNumberException.csAdds InvalidCardNumberException
src/Abstractions/PaymentProviders/Exceptions/InvalidCVVException.csAdds InvalidCVVException
src/Abstractions/PaymentProviders/Exceptions/InvalidAmountException.csAdds InvalidAmountException
src/Abstractions/PaymentProviders/Exceptions/DuplicatePaymentException.csAdds DuplicatePaymentException
src/Abstractions/PaymentProviders/Entities/PaymentRequest.csImplements PaymentRequest entity and validation methods
Comments suppressed due to low confidence (1)

src/Abstractions/PaymentProviders/ValueObjects/CardNumber.cs:36

  • Validation only checks length; consider stripping whitespace and verifying all characters are digits and potentially applying the Luhn algorithm to prevent invalid card numbers.
return Regex.Replace(number, "@\\s+", "").Length == 16;

/// <returns>A masked card number</returns>
public static string Mask(CardNumber cardNumber)
{
var visiblePart = cardNumber.Number.Substring(0 + cardNumber.Number.Length - UnmaskedDigits);

CopilotAIMay 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 0 + in the Substring call is redundant; simplify to cardNumber.Number.Substring(cardNumber.Number.Length - UnmaskedDigits) for clarity.

Suggested change
varvisiblePart=cardNumber.Number.Substring(0+cardNumber.Number.Length-UnmaskedDigits);
varvisiblePart=cardNumber.Number.Substring(cardNumber.Number.Length-UnmaskedDigits);

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +72
if (!string.IsNullOrEmpty(mobileNumber) && mobileNumber.Length != 11)
throw new ArgumentException("Mobile number must be 11 digits.", nameof(mobileNumber));

CopilotAIMay 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mobile number is only length-checked; you should also enforce that it contains only digits (e.g. with a regex) to avoid invalid input.

Suggested change
if(!string.IsNullOrEmpty(mobileNumber)&&mobileNumber.Length!=11)
thrownewArgumentException("Mobile number must be 11 digits.",nameof(mobileNumber));
if(!string.IsNullOrEmpty(mobileNumber))
{
if(mobileNumber.Length!=11||!Regex.IsMatch(mobileNumber,@"^\d{11}$"))
thrownewArgumentException("Mobile number must be exactly 11 digits and contain only numeric characters.",nameof(mobileNumber));
}

Copilot uses AI. Check for mistakes.
{ "AUD", "Australian dollar (A$)" },
{ "BRL", "Brazilian real (R$)" },
{ "CAD", "Canadian dollar (C$)" },
{ "CNY", "Chinese Renmenbi (¥)" },

CopilotAIMay 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in currency name: "Renmenbi" should be "Renminbi".

Suggested change
{"CNY","Chinese Renmenbi (¥)"},
{"CNY","Chinese Renminbi (¥)"},

Copilot uses AI. Check for mistakes.
throw new InvalidExpiryDateException($"{month}/{year} is not a valid expiry date");
}

//TODO: it works only on forieng countries cards, should be globalized by culture

CopilotAIMay 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "forieng" should be "foreign".

Suggested change
//TODO: it works only on forieng countries cards, should be globalized by culture
//TODO: it works only on foreign countries' cards, should be globalized by culture

Copilot uses AI. Check for mistakes.
throw new InvalidAmountException();

if (string.IsNullOrEmpty(currency) || !BaseCurrency.CurrencySymbols.ContainsKey(currency.ToUpper()))
throw new InvalidCurrencyException();

CopilotAIMay 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing InvalidCurrencyException without a message can make debugging harder; consider including the invalid currency code in the exception message.

Suggested change
thrownewInvalidCurrencyException();
thrownewInvalidCurrencyException($"Invalid currency code: '{currency}'.");

Copilot uses AI. Check for mistakes.
}

public void Validate()
{

CopilotAIMay 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation logic in Validate() duplicates the checks in Create; consider centralizing field validation in one place or reusing the factory checks to avoid drift.

Suggested change
{
{
ValidateFields();
}
privatevoidValidateFields()
{

Copilot uses AI. Check for mistakes.
@imhmdiimhmdi closed this May 24, 2025
@imhmdi
imhmdi deleted the feat/create-domain-valueobjects-tests branch May 24, 2025 20:10
@imhmdi
imhmdi restored the feat/create-domain-valueobjects-tests branch May 27, 2025 13:24
@imhmdiimhmdi reopened this May 27, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@hamedzsmm@imhmdi