Merged PR 51: #27 get last receipt, get minimalUser, get my user

#27 get last receipt, get minimalUser, get my user

Related work items: #27
This commit is contained in:
Dominic Villemure
2024-05-12 20:52:46 +00:00
17 changed files with 216 additions and 24 deletions

View File

@@ -6,6 +6,6 @@ namespace Hutopy.Application.Common.Interfaces;
public interface IStripeService
{
public Task<string> CreateCheckoutSession(int amount, string currency);
public Task<string> CreateCheckoutSession(int amount, string creatorId, string currency);
public Result ValidateTransaction(ConfirmStripeTransactionCommand request);
}

View File

@@ -56,6 +56,7 @@ public class ConfirmStripeTransactionCommandHandler(
if (stripeConfirmation.Succeeded)
{
lastTransaction.IsConfirmed = true;
}
lastTransaction.Paid = request.Data.Object.Paid;
lastTransaction.StripeChargeId = request.Data.Object.Id;
lastTransaction.StripeEventId = request.Id;
@@ -64,7 +65,6 @@ public class ConfirmStripeTransactionCommandHandler(
lastTransaction.StripePaymentMethod = request.Data.Object.Payment_method;
lastTransaction.StripeBillingDetailEmail = request.Data.Object.Billing_details.Email;
lastTransaction.StripeBillingDetailName = request.Data.Object.Billing_details.Name;
}
await dbContext.SaveChangesAsync(cancellationToken);

View File

@@ -18,7 +18,7 @@ public class CreateSessionCheckoutCommandHandler(
{
public async Task<string> Handle(CreateSessionCheckoutCommand request, CancellationToken cancellationToken)
{
var stripeSecret = await stripeService.CreateCheckoutSession(request.Amount, request.Currency);
var stripeSecret = await stripeService.CreateCheckoutSession(request.Amount, request.CreatorId, request.Currency);
// ReSharper disable once PossibleLossOfFraction
decimal priceInDollars = (request.Amount / 100);

View File

@@ -0,0 +1,29 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.Stripe.Queries;
public record GetMyLastReceiptQuery : IRequest<MyLastReceiptDto>
{
public string Email { get; set; } = string.Empty;
public string CreatorId { get; set; } = string.Empty;
};
public class GetMyLastReceiptQueryHandler(
IApplicationDbContext dbContext
)
: IRequestHandler<GetMyLastReceiptQuery, MyLastReceiptDto>
{
public async Task<MyLastReceiptDto> Handle(GetMyLastReceiptQuery request, CancellationToken cancellationToken)
{
var lastTransaction = await dbContext.UserTransactions.OrderBy(x => x.Created)
.LastOrDefaultAsync(x => x.ApplicationUserId == request.CreatorId && x.StripeBillingDetailEmail == request.Email,
cancellationToken);
var receiptUrl = new MyLastReceiptDto
{
ReceiptUrl = lastTransaction?.StripeReceiptUrl ?? "",
};
return receiptUrl;
}
}

View File

@@ -0,0 +1,6 @@
namespace Hutopy.Application.Stripe.Queries;
public class MyLastReceiptDto
{
public string ReceiptUrl { get; set; }
}

View File

@@ -18,12 +18,11 @@ public class GetCurrentUserQueryHandler(
var currentUserId = new Guid(identityUser?.Id ?? "");
var transactions = await context.UserTransactions
.Where(x => x.Id == currentUserId)
.Where(x => x.ApplicationUserId == currentUserId.ToString())
.OrderBy(x => x.LastModified)
.ProjectTo<UserTransactionDto>(mapper.ConfigurationProvider)
.ToListAsync(cancellationToken);
var user = new UserDto()
{
Id = currentUserId,

View File

@@ -7,6 +7,7 @@ public class UserDto
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string UserName { get; init; } = String.Empty;
public List<UserTransactionDto> UserTransactions { get; init; } = [];
}

View File

@@ -0,0 +1,28 @@
using Hutopy.Domain.Interfaces;
namespace Hutopy.Application.Users.Queries.GetMinimalUser;
public record GetMinimalUserQuery : IRequest<MinimalUserDto>
{
public string UserId { get; set; } = string.Empty;
};
public class GetMinimalUserQueryHandler(
IUserService userService
)
: IRequestHandler<GetMinimalUserQuery, MinimalUserDto>
{
public async Task<MinimalUserDto> Handle(GetMinimalUserQuery request, CancellationToken cancellationToken)
{
var identityUser = await userService.FindUserByIdAsync(request.UserId);
var user = new MinimalUserDto()
{
FirstName = identityUser?.FirstName ?? "",
LastName = identityUser?.LastName ?? "",
UserName = identityUser?.UserName ?? ""
};
return user;
}
}

View File

@@ -0,0 +1,8 @@
namespace Hutopy.Application.Users.Queries.GetMinimalUser;
public class MinimalUserDto
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string UserName { get; init; } = String.Empty;
}

View File

@@ -7,7 +7,7 @@ public class UserTransaction : BaseAuditableEntity
public string TipMessage { get; set; } = string.Empty;
// Foreign key to ApplicationUser
public string ApplicationUserId { get; set; } = string.Empty;
public required string ApplicationUserId { get; set; }
public bool IsConfirmed { get; set; }
public string StripeEventId { get; set; } = string.Empty;
public string StripeChargeId { get; set; } = string.Empty;

View File

@@ -1,11 +1,13 @@
using Hutopy.Domain.Interfaces;
using System.Security.Claims;
using Hutopy.Domain.Interfaces;
using Hutopy.Domain.Models;
using Hutopy.Infrastructure.Identity;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
namespace Hutopy.Infrastructure.Services;
public class UserService(UserManager<ApplicationUser> userManager) : IUserService
public class UserService(UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor) : IUserService
{
public async Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
{
@@ -38,7 +40,7 @@ public class UserService(UserManager<ApplicationUser> userManager) : IUserServic
UserName = response.UserName,
FirstName = response.FirstName,
LastName = response.LastName,
Email = response.Email
Email = response.Email,
};
return userModel;
@@ -47,13 +49,13 @@ public class UserService(UserManager<ApplicationUser> userManager) : IUserServic
public async Task<UserModel?> GetCurrentUserAsync()
{
// todo: Get the id of the user doing the request.
var userId = "";
if (string.IsNullOrEmpty(userId))
var currentUserId = contextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(currentUserId))
{
return null;
}
return await FindUserByIdAsync(userId);
return await FindUserByIdAsync(currentUserId);
}
public async Task<UserModel?> FindUserByEmailAsync(string email)

View File

@@ -9,7 +9,6 @@ namespace Hutopy.Infrastructure.Stripe;
public class StripeService : IStripeService
{
const string EndpointSecret = "";
private readonly IHttpContextAccessor _httpContextAccessor;
public StripeService(IHttpContextAccessor httpContextAccessor)
@@ -18,7 +17,7 @@ public class StripeService : IStripeService
StripeConfiguration.ApiKey = "";
}
public async Task<string> CreateCheckoutSession(int amount, string currency = "cad")
public async Task<string> CreateCheckoutSession(int amount, string creatorId, string currency = "cad")
{
var options = new SessionCreateOptions
{
@@ -38,7 +37,9 @@ public class StripeService : IStripeService
],
Mode = "payment",
UiMode = "embedded",
ReturnUrl = "https://hutopy.ca/paymentcompleted",
ReturnUrl = $"https://hutopy.ca/paymentcompleted?creatorId={creatorId}",
InvoiceCreation = new SessionInvoiceCreationOptions(){ Enabled = true},
ClientReferenceId = creatorId
};
var service = new SessionService();
@@ -57,7 +58,6 @@ public class StripeService : IStripeService
}
return new Result(false, new List<string>());
}
catch (StripeException e)
{

View File

@@ -7,6 +7,7 @@ public class GetMyUser : EndpointGroupBase
public override void Map(WebApplication app)
{
app.MapGroup(this)
.RequireAuthorization()
.MapGet(GetCurrentUser);
}

View File

@@ -1,4 +1,5 @@
using Hutopy.Application.Stripe.Commands;
using Hutopy.Application.Stripe.Queries;
namespace Hutopy.Web.Endpoints;
@@ -8,6 +9,7 @@ public class Stripe : EndpointGroupBase
{
app.MapGroup(this)
.MapPost(ConfirmTransaction, "/confirmTransaction")
.MapGet(GetMyLastReceipt, "/getMyLastReceipt")
.MapPost(CreateSessionCheckout);
}
@@ -20,4 +22,9 @@ public class Stripe : EndpointGroupBase
{
return await sender.Send(command);
}
private static async Task<MyLastReceiptDto> GetMyLastReceipt(ISender sender, [AsParameters] GetMyLastReceiptQuery query)
{
return await sender.Send(query);
}
}

View File

@@ -1,4 +1,5 @@
using Hutopy.Application.Users.Commands;
using Hutopy.Application.Users.Queries.GetMinimalUser;
using Hutopy.Domain.Interfaces;
using Hutopy.Infrastructure.Identity;
@@ -10,6 +11,7 @@ public class Users : EndpointGroupBase
{
app.MapGroup(this)
.MapPost(CreateUser)
.MapGet(GetMinimalUser)
.MapIdentityApi<ApplicationUser>();
}
@@ -18,4 +20,9 @@ public class Users : EndpointGroupBase
await userService.CreateUserAsync(command.EmailAddress, command.UserName, command.FirstName, command.LastName, command.Password);
return await sender.Send(command);
}
private static async Task<MinimalUserDto> GetMinimalUser(ISender sender, [AsParameters] GetMinimalUserQuery query)
{
return await sender.Send(query);
}
}

View File

@@ -23,7 +23,12 @@
}
}
}
},
"security": [
{
"JWT": []
}
]
}
},
"/api/JoinUs": {
@@ -131,6 +136,48 @@
}
}
},
"/api/Stripe/getMyLastReceipt": {
"get": {
"tags": [
"Stripe"
],
"operationId": "GetMyLastReceipt",
"parameters": [
{
"name": "Email",
"in": "query",
"required": true,
"schema": {
"type": "string",
"nullable": true
},
"x-position": 1
},
{
"name": "CreatorId",
"in": "query",
"required": true,
"schema": {
"type": "string",
"nullable": true
},
"x-position": 2
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MyLastReceiptDto"
}
}
}
}
}
}
},
"/api/Stripe": {
"post": {
"tags": [
@@ -194,6 +241,36 @@
}
}
}
},
"get": {
"tags": [
"Users"
],
"operationId": "GetMinimalUser",
"parameters": [
{
"name": "UserId",
"in": "query",
"required": true,
"schema": {
"type": "string",
"nullable": true
},
"x-position": 1
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MinimalUserDto"
}
}
}
}
}
}
},
"/api/Users/register": {
@@ -625,6 +702,9 @@
"lastName": {
"type": "string"
},
"userName": {
"type": "string"
},
"userTransactions": {
"type": "array",
"items": {
@@ -814,6 +894,15 @@
}
}
},
"MyLastReceiptDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"receiptUrl": {
"type": "string"
}
}
},
"CreateSessionCheckoutCommand": {
"type": "object",
"additionalProperties": false,
@@ -854,6 +943,21 @@
}
}
},
"MinimalUserDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"userName": {
"type": "string"
}
}
},
"HttpValidationProblemDetails": {
"allOf": [
{