Skip to content

Commit

Permalink
Merge pull request #105 from soat-fiap/73_get_payment_status
Browse files Browse the repository at this point in the history
feat: get payment status #73
  • Loading branch information
italopessoa committed Jun 19, 2024
2 parents feb8c0c + 86d01f4 commit 0725d61
Show file tree
Hide file tree
Showing 18 changed files with 203 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ public PaymentController(IPaymentService paymentService)
[FromQuery] Guid orderId, CancellationToken cancellationToken)
{
var payment = await _paymentService.CreateOrderPaymentAsync(orderId);
return Ok(new PaymentViewModel(payment.Id.Code, payment.PaymentType, payment.QrCode));
return Ok(new PaymentViewModel(payment.Id.Code, payment.QrCode));
}

/// <summary>
/// Get the payment status
/// </summary>
/// <param name="id">Payment Id.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Payment status</returns>
[HttpGet("{id}/status")]
public async Task<ActionResult<PaymentStatusViewModel>> GetStatus(
string id, CancellationToken cancellationToken)
{
var payment = await _paymentService.GetPaymentAsync(id);

if (payment is null)
return NotFound();

return Ok((PaymentStatusViewModel)(int)payment.Status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2024, Italo Pessoa (https://github.com/italopessoa)
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.

namespace FIAP.TechChallenge.ByteMeBurger.Api.Model;

/// <summary>
/// Represents the payment status view model.
/// </summary>
public enum PaymentStatusViewModel
{
Pending = 0,
InProgress = 1,
Approved = 2,
Rejected = 3,
Paid = 4,
Cancelled = 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2024, Italo Pessoa (https://github.com/italopessoa)
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.

namespace FIAP.TechChallenge.ByteMeBurger.Api.Model;

public enum PaymentTypeViewModel
{
Test = 0,
MercadoPago = 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

namespace FIAP.TechChallenge.ByteMeBurger.Api.Model;

public record PaymentViewModel(string PaymentId, int PaymentType, string QrCode);
public record PaymentViewModel(string PaymentId, string QrCode);
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ public async Task<Payment> CreateOrderPaymentAsync(Guid orderId)
await _paymentRepository.SaveAsync(payment);
return payment;
}

public async Task<Payment?> GetPaymentAsync(string paymentId)
{
return await _paymentRepository.GetPaymentAsync(paymentId);
}
}
20 changes: 5 additions & 15 deletions src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,30 @@ namespace FIAP.TechChallenge.ByteMeBurger.Domain.Entities;

public class Payment : Entity<PaymentId>, IAggregateRoot
{
public PaymentStatus Status { get; set; }

public string Type { get; set; }

/// <summary>
/// Local=0, MercadoPago=1
/// </summary>
public int PaymentType { get; set; }
public PaymentType PaymentType { get; set; }

public string QrCode { get; set; }

public decimal Amount { get; set; }

public DateTime Created { get; set; }

public Payment()
{
}
public PaymentStatus Status { get; set; }

public Payment(PaymentId id)
: base(id)
public Payment()

Check warning on line 26 in src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Type' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 26 in src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'QrCode' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 26 in src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Type' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 26 in src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'QrCode' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
{
Id = id;
Status = PaymentStatus.Pending;
Created = DateTime.UtcNow;
}

public Payment(PaymentId id, string qrCode, decimal amount)
public Payment(PaymentId id, string qrCode, decimal amount, PaymentType paymentType = PaymentType.Test)

Check warning on line 30 in src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Type' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 30 in src/FIAP.TechChallenge.ByteMeBurger.Domain/Entities/Payment.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Type' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
: base(id)
{
Id = id;
Status = PaymentStatus.Pending;
QrCode = qrCode;
Amount = amount;
Created = DateTime.UtcNow;
PaymentType = paymentType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public interface IPaymentRepository
{
Task<Payment> SaveAsync(Payment payment);

Task<PaymentStatus> GetPaymentStatusAsync(string paymentId);
Task<Payment?> GetPaymentAsync(string paymentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ namespace FIAP.TechChallenge.ByteMeBurger.Domain.Interfaces;
public interface IPaymentService
{
Task<Payment> CreateOrderPaymentAsync(Guid orderId);

Task<Payment?> GetPaymentAsync(string paymentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ namespace FIAP.TechChallenge.ByteMeBurger.Domain.ValueObjects;
public enum PaymentStatus
{
Pending = 0,
InProgress,
Approved,
Rejected,
Cancelled
InProgress = 1,
Approved = 2,
Rejected = 3,
Paid = 4,
Cancelled = 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2024, Italo Pessoa (https://github.com/italopessoa)
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.

namespace FIAP.TechChallenge.ByteMeBurger.Domain.ValueObjects;

public enum PaymentType
{
Test = 0,
MercadoPago = 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace FIAP.TechChallenge.ByteMeBurger.MercadoPago.Gateway;
public class MercadoPagoService : IPaymentGateway
{
private readonly MercadoPagoOptions _mercadoPagoOptions;
private const decimal IntegrationPrice = 0.01M;

public MercadoPagoService(IOptions<MercadoPagoOptions> mercadoPagoOptions)
{
Expand All @@ -47,13 +48,15 @@ public async Task<DomainPayment> CreatePaymentAsync(Order order)
}
: MapPaymentPayerRequest(order);

var items = MapPaymentItemRequests(order);
var items = MapPaymentItemRequests(order).ToList();

var totalAmount = items.Sum(i => i.Quantity * i.UnitPrice);
var additionalInfo = new PaymentAdditionalInfoRequest
{
Items = items.ToList(),
};

var paymentCreateRequest = MapPaymentCreateRequest(order, paymentPayerRequest, additionalInfo);
var paymentCreateRequest = MapPaymentCreateRequest(order, paymentPayerRequest, additionalInfo, totalAmount!.Value);
var client = new PaymentClient();
var mercadoPagoPayment = await client.CreateAsync(paymentCreateRequest, requestOptions);

Expand All @@ -65,16 +68,14 @@ public async Task<DomainPayment> CreatePaymentAsync(Order order)
{
Status = status,
Id = new PaymentId(mercadoPagoPayment.Id.ToString()!, order.Id),
PaymentType = 1,
PaymentType = PaymentType.MercadoPago,
QrCode = mercadoPagoPayment.PointOfInteraction.TransactionData.QrCode
};
}

private static PaymentPayerRequest MapPaymentPayerRequest(Order order)
=> new()
{
Type = "individual",
EntityType = "customer",
Email = order.Customer.Email,
FirstName = order.Customer.Name,
LastName = "User",
Expand All @@ -94,7 +95,7 @@ private static IEnumerable<PaymentItemRequest> MapPaymentItemRequests(Order orde
Description = item.ProductName,
CategoryId = "food",
Quantity = item.Quantity,
UnitPrice = item.UnitPrice,
UnitPrice = IntegrationPrice,
Warranty = false,
CategoryDescriptor = new PaymentCategoryDescriptorRequest
{
Expand All @@ -104,18 +105,18 @@ private static IEnumerable<PaymentItemRequest> MapPaymentItemRequests(Order orde
}
);

private PaymentCreateRequest MapPaymentCreateRequest(Order order, PaymentPayerRequest paymentPayerRequest2,
PaymentAdditionalInfoRequest paymentAdditionalInfoRequest)
private PaymentCreateRequest MapPaymentCreateRequest(Order order, PaymentPayerRequest payer,
PaymentAdditionalInfoRequest paymentAdditionalInfoRequest, decimal amount)
=> new()
{
Description = $"Payment for Order {order.TrackingCode.Value}",
ExternalReference = order.TrackingCode.Value,
Installments = 1,
NotificationUrl = _mercadoPagoOptions.NotificationUrl,
Payer = paymentPayerRequest2,
Payer = payer,
PaymentMethodId = "pix",
StatementDescriptor = "tech challenge restaurant order",
TransactionAmount = (decimal?)0.01,
TransactionAmount = amount,
AdditionalInfo = paymentAdditionalInfoRequest,
DateOfExpiration = DateTime.UtcNow.AddMinutes(5)
};
Expand Down
6 changes: 4 additions & 2 deletions src/FIAP.TechChallenge.ByteMeBurger.Persistence/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace FIAP.TechChallenge.ByteMeBurger.Persistence;

public static class Constants
internal static class Constants
{
internal const string GetOrderByIdQuery = @"select o.Id,
o.Status,
Expand Down Expand Up @@ -59,7 +59,9 @@ public static class Constants
"insert into OrderItems (OrderId, ProductId, ProductName, UnitPrice, Quantity) " +
"values (@OrderId, @ProductId, @ProductName, @UnitPrice, @Quantity);";

public const string InsertPaymentQuery =
internal const string InsertPaymentQuery =
"insert into Payments (Id, OrderId, Status, Created, PaymentType, Amount) " +
"values (@Id, @OrderId, @Status, @Created, @PaymentType, @Amount);";

internal const string GetPaymentQuery = "select * from Payments where Id = @Id;";
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ public async Task<Payment> SaveAsync(Payment payment)
logger.LogInformation("Persisting Payment with Id: {Id} for Order {OrderId}", payment.Id.Code,
payment.Id.OrderId);
dbConnection.Open();

var transaction = dbConnection.BeginTransaction();
{
try
{
var paymentDao = new PaymentDAO(payment.Id.Code, payment.Id.OrderId, (int)payment.Status,
payment.PaymentType, payment.Amount, payment.Created, null);
(int)payment.PaymentType, payment.Amount, payment.Created, null);
await dbConnection.ExecuteAsync(Constants.InsertPaymentQuery, paymentDao);

transaction.Commit();
Expand All @@ -49,8 +50,29 @@ public async Task<Payment> SaveAsync(Payment payment)
}
}

public Task<PaymentStatus> GetPaymentStatusAsync(string paymentId)
public async Task<Payment?> GetPaymentAsync(string paymentId)
{
throw new NotImplementedException();
logger.LogInformation("Getting Payment with ID: {PaymentId}", paymentId);

var paymentDao = await dbConnection.QuerySingleOrDefaultAsync<PaymentDAO>(
Constants.GetPaymentQuery,
param: new { Id = paymentId }
);

if (paymentDao is null)
{
return null;
}

logger.LogInformation("Payment with ID: {PaymentId} retrieved", paymentId);
return new Payment()
{
Id = new PaymentId(paymentDao.Id, paymentDao.OrderId),
PaymentType = (PaymentType)paymentDao.PaymentType,
// TODO QrCode = paymentDao.QrCode
Amount = paymentDao.Amount,
Created = paymentDao.Created,
Status = (PaymentStatus)paymentDao.Status
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,32 @@ public async void Create_Success()
var paymentViewModel = response.Result.As<OkObjectResult>().Value.As<PaymentViewModel>();

paymentViewModel.PaymentId.Should().Be(payment.Id.Code);
paymentViewModel.PaymentType.Should().Be(payment.PaymentType);
paymentViewModel.QrCode.Should().Be(payment.QrCode);
}
}

[Fact]
public async void GetStatus_Success()
{
// Arrange
var paymentId = new PaymentId("123", Guid.NewGuid());
var payment = new Payment(paymentId, "qrcode", 10, PaymentType.MercadoPago)
{
Status = PaymentStatus.Paid
};
_serviceMock.Setup(p => p.GetPaymentAsync(It.IsAny<string>()))
.ReturnsAsync(payment);

// Act
var response = await _target.GetStatus(paymentId.Code, CancellationToken.None);

// Assert
using (new AssertionScope())
{
response.Result.Should().BeOfType<OkObjectResult>();
var status = response.Result.As<OkObjectResult>().Value.As<PaymentStatusViewModel>();

status.Should().Be(PaymentStatusViewModel.Paid);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@
<ProjectReference Include="..\FIAP.TechChallenge.ByteMeBurger.Application.Test\FIAP.TechChallenge.ByteMeBurger.Application.Test.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Repository\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,25 @@ public async Task CreateOrderPaymentAsync_Success()
_mockPaymentRepository.Verify(r => r.SaveAsync(It.IsAny<Payment>()), Times.Once);
}
}

[Fact]
public async Task GetPaymentAsync_Success()
{
// Arrange
var expectedPayment = new Fixture().Create<Payment>();
_mockPaymentRepository.Setup(p => p.GetPaymentAsync(It.IsAny<string>()))
.ReturnsAsync(expectedPayment)
.Verifiable();

// Act
var result = await _target.GetPaymentAsync("paymentId");

// Assert
using (new AssertionScope())
{
result.Should().NotBeNull();
result.Should().Be(expectedPayment);
_mockPaymentRepository.Verify();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
Expand Down
Loading

0 comments on commit 0725d61

Please sign in to comment.