danylokhodus commited on
Commit
8a2241a
·
1 Parent(s): 31c319a

order update

Browse files
Application/DTOs/OrderDTOs/PayOrderDTO.cs CHANGED
@@ -21,5 +21,10 @@ namespace Application.DTOs.OrderDTOs
21
  /// Payment method: PayPal, GooglePay, or Stripe
22
  /// </summary>
23
  public string PaymentMethod { get; set; } = string.Empty;
 
 
 
 
 
24
  }
25
  }
 
21
  /// Payment method: PayPal, GooglePay, or Stripe
22
  /// </summary>
23
  public string PaymentMethod { get; set; } = string.Empty;
24
+
25
+ /// <summary>
26
+ /// Optional token for Stripe payments
27
+ /// </summary>
28
+ public string? StripeCardToken { get; set; }
29
  }
30
  }
Application/DTOs/UserDTOs/UserProfileDTO.cs CHANGED
@@ -15,5 +15,6 @@ namespace Application.DTOs.UserDTOs
15
  public string? Address { get; set; }
16
  public double? Latitude { get; set; }
17
  public double? Longitude { get; set; }
 
18
  }
19
  }
 
15
  public string? Address { get; set; }
16
  public double? Latitude { get; set; }
17
  public double? Longitude { get; set; }
18
+ public decimal Balance { get; set; }
19
  }
20
  }
Application/Services/OrderService.cs CHANGED
@@ -88,7 +88,7 @@ namespace Application.Services
88
  IsProductPaid = orderDto.IsProductPaid,
89
  DeliveryPayer = orderDto.DeliveryPayer,
90
  IsDeliveryPaid = false,
91
- Status = OrderStatus.Pending,
92
  SenderId = senderId,
93
  RecipientId = orderDto.RecipientId,
94
  PickupNodeId = sender.PersonalNodeId.Value,
@@ -312,6 +312,8 @@ namespace Application.Services
312
  // Define valid status transitions
313
  return currentStatus switch
314
  {
 
 
315
  OrderStatus.Pending => newStatus == OrderStatus.Processing || newStatus == OrderStatus.Cancelled,
316
  OrderStatus.Processing => newStatus == OrderStatus.EnRoute || newStatus == OrderStatus.Cancelled,
317
  OrderStatus.EnRoute => newStatus == OrderStatus.Delivered || newStatus == OrderStatus.Cancelled,
@@ -341,6 +343,11 @@ namespace Application.Services
341
  throw new InvalidOperationException($"Cannot execute order with status {order.Status}. Only Pending orders can be executed.");
342
  }
343
 
 
 
 
 
 
344
  // Get pickup and dropoff nodes
345
  var pickupNode = await _nodeRepository.GetByIdAsync(order.PickupNodeId);
346
  var dropoffNode = await _nodeRepository.GetByIdAsync(order.DropoffNodeId);
 
88
  IsProductPaid = orderDto.IsProductPaid,
89
  DeliveryPayer = orderDto.DeliveryPayer,
90
  IsDeliveryPaid = false,
91
+ Status = orderDto.DeliveryPayer == DeliveryPayer.Sender ? OrderStatus.AwaitingPayment : OrderStatus.AwaitingConfirmation,
92
  SenderId = senderId,
93
  RecipientId = orderDto.RecipientId,
94
  PickupNodeId = sender.PersonalNodeId.Value,
 
312
  // Define valid status transitions
313
  return currentStatus switch
314
  {
315
+ OrderStatus.AwaitingPayment => newStatus == OrderStatus.AwaitingConfirmation || newStatus == OrderStatus.Cancelled,
316
+ OrderStatus.AwaitingConfirmation => newStatus == OrderStatus.Pending || newStatus == OrderStatus.Cancelled,
317
  OrderStatus.Pending => newStatus == OrderStatus.Processing || newStatus == OrderStatus.Cancelled,
318
  OrderStatus.Processing => newStatus == OrderStatus.EnRoute || newStatus == OrderStatus.Cancelled,
319
  OrderStatus.EnRoute => newStatus == OrderStatus.Delivered || newStatus == OrderStatus.Cancelled,
 
343
  throw new InvalidOperationException($"Cannot execute order with status {order.Status}. Only Pending orders can be executed.");
344
  }
345
 
346
+ if (!order.IsProductPaid || !order.IsDeliveryPaid)
347
+ {
348
+ throw new InvalidOperationException("Cannot execute order until both product and delivery are paid.");
349
+ }
350
+
351
  // Get pickup and dropoff nodes
352
  var pickupNode = await _nodeRepository.GetByIdAsync(order.PickupNodeId);
353
  var dropoffNode = await _nodeRepository.GetByIdAsync(order.DropoffNodeId);
Application/Services/UserService.cs CHANGED
@@ -57,7 +57,8 @@ namespace Application.Services
57
  PersonalNodeId = user.PersonalNodeId,
58
  Address = user.PersonalNode?.Name,
59
  Latitude = user.PersonalNode?.Latitude,
60
- Longitude = user.PersonalNode?.Longitude
 
61
  };
62
  }
63
 
@@ -79,7 +80,8 @@ namespace Application.Services
79
  PersonalNodeId = user.PersonalNodeId,
80
  Address = user.PersonalNode?.Name,
81
  Latitude = user.PersonalNode?.Latitude,
82
- Longitude = user.PersonalNode?.Longitude
 
83
  }).ToList();
84
  }
85
 
@@ -126,7 +128,8 @@ namespace Application.Services
126
  PersonalNodeId = user.PersonalNodeId,
127
  Address = user.PersonalNode?.Name,
128
  Latitude = user.PersonalNode?.Latitude,
129
- Longitude = user.PersonalNode?.Longitude
 
130
  };
131
  }
132
 
@@ -244,7 +247,8 @@ namespace Application.Services
244
  PersonalNodeId = user.PersonalNodeId,
245
  Address = user.PersonalNode?.Name,
246
  Latitude = user.PersonalNode?.Latitude,
247
- Longitude = user.PersonalNode?.Longitude
 
248
  };
249
  }
250
 
@@ -299,7 +303,8 @@ namespace Application.Services
299
  PersonalNodeId = user.PersonalNodeId,
300
  Address = user.PersonalNode?.Name,
301
  Latitude = user.PersonalNode?.Latitude,
302
- Longitude = user.PersonalNode?.Longitude
 
303
  };
304
  }
305
 
 
57
  PersonalNodeId = user.PersonalNodeId,
58
  Address = user.PersonalNode?.Name,
59
  Latitude = user.PersonalNode?.Latitude,
60
+ Longitude = user.PersonalNode?.Longitude,
61
+ Balance = user.Balance
62
  };
63
  }
64
 
 
80
  PersonalNodeId = user.PersonalNodeId,
81
  Address = user.PersonalNode?.Name,
82
  Latitude = user.PersonalNode?.Latitude,
83
+ Longitude = user.PersonalNode?.Longitude,
84
+ Balance = user.Balance
85
  }).ToList();
86
  }
87
 
 
128
  PersonalNodeId = user.PersonalNodeId,
129
  Address = user.PersonalNode?.Name,
130
  Latitude = user.PersonalNode?.Latitude,
131
+ Longitude = user.PersonalNode?.Longitude,
132
+ Balance = user.Balance
133
  };
134
  }
135
 
 
247
  PersonalNodeId = user.PersonalNodeId,
248
  Address = user.PersonalNode?.Name,
249
  Latitude = user.PersonalNode?.Latitude,
250
+ Longitude = user.PersonalNode?.Longitude,
251
+ Balance = user.Balance
252
  };
253
  }
254
 
 
303
  PersonalNodeId = user.PersonalNodeId,
304
  Address = user.PersonalNode?.Name,
305
  Latitude = user.PersonalNode?.Latitude,
306
+ Longitude = user.PersonalNode?.Longitude,
307
+ Balance = user.Balance
308
  };
309
  }
310
 
Entities/Enums/OrderStatus.cs CHANGED
@@ -1,5 +1,7 @@
1
- public enum OrderStatus
2
  {
 
 
3
  Pending,
4
  Processing,
5
  EnRoute,
 
1
+ public enum OrderStatus
2
  {
3
+ AwaitingPayment,
4
+ AwaitingConfirmation,
5
  Pending,
6
  Processing,
7
  EnRoute,
Entities/Models/User.cs CHANGED
@@ -13,6 +13,8 @@ namespace Entities.Models
13
  public string? PhoneNumber { get; set; }
14
  public string PasswordHash { get; set; }
15
  public UserRole? Role { get; set; }
 
 
16
 
17
  // User's personal location node
18
  public int? PersonalNodeId { get; set; }
 
13
  public string? PhoneNumber { get; set; }
14
  public string PasswordHash { get; set; }
15
  public UserRole? Role { get; set; }
16
+
17
+ public decimal Balance { get; set; } = 0;
18
 
19
  // User's personal location node
20
  public int? PersonalNodeId { get; set; }
RobDeliveryAPI/Controllers/PaymentsController.cs CHANGED
@@ -16,13 +16,16 @@ public class PaymentsController : ControllerBase
16
  {
17
  private readonly IPaymentProcessorService _paymentProcessorService;
18
  private readonly IOrderRepository _orderRepository;
 
19
 
20
  public PaymentsController(
21
  IPaymentProcessorService paymentProcessorService,
22
- IOrderRepository orderRepository)
 
23
  {
24
  _paymentProcessorService = paymentProcessorService;
25
  _orderRepository = orderRepository;
 
26
  }
27
 
28
  /// <summary>
@@ -177,6 +180,26 @@ public class PaymentsController : ControllerBase
177
  // Check if there's anything to pay
178
  if (totalAmount == 0)
179
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  return BadRequest(new PaymentResultDTO
181
  {
182
  Success = false,
@@ -188,13 +211,13 @@ public class PaymentsController : ControllerBase
188
  });
189
  }
190
 
191
- // Process payment through payment processor
192
  var paymentRequest = new PaymentRequestDTO
193
  {
194
  Amount = totalAmount,
195
  Currency = "UAH",
196
  OrderId = order.Id,
197
- PaymentMethod = paymentDto.PaymentMethod
 
198
  };
199
 
200
  var paymentResult = await _paymentProcessorService.ProcessPaymentAsync(paymentRequest);
@@ -214,12 +237,29 @@ public class PaymentsController : ControllerBase
214
  if (willPayProduct)
215
  {
216
  order.IsProductPaid = true;
 
 
 
 
 
 
 
217
  }
218
  if (willPayDelivery)
219
  {
220
  order.IsDeliveryPaid = true;
221
  }
222
 
 
 
 
 
 
 
 
 
 
 
223
  await _orderRepository.UpdateAsync(order);
224
 
225
  // Return success result
 
16
  {
17
  private readonly IPaymentProcessorService _paymentProcessorService;
18
  private readonly IOrderRepository _orderRepository;
19
+ private readonly IUserRepository _userRepository;
20
 
21
  public PaymentsController(
22
  IPaymentProcessorService paymentProcessorService,
23
+ IOrderRepository orderRepository,
24
+ IUserRepository userRepository)
25
  {
26
  _paymentProcessorService = paymentProcessorService;
27
  _orderRepository = orderRepository;
28
+ _userRepository = userRepository;
29
  }
30
 
31
  /// <summary>
 
180
  // Check if there's anything to pay
181
  if (totalAmount == 0)
182
  {
183
+ if (order.Status == OrderStatus.AwaitingConfirmation && order.IsProductPaid && order.IsDeliveryPaid)
184
+ {
185
+ // Allow simple confirmation if everything is already paid
186
+ order.Status = OrderStatus.Pending;
187
+ await _orderRepository.UpdateAsync(order);
188
+
189
+ return Ok(new PaymentResultDTO
190
+ {
191
+ Success = true,
192
+ TransactionId = "CONFIRM_ONLY",
193
+ PaymentMethod = paymentDto.PaymentMethod,
194
+ Amount = 0,
195
+ Currency = "UAH",
196
+ OrderId = order.Id,
197
+ ProductPaid = true,
198
+ DeliveryPaid = true,
199
+ ProcessedAt = DateTime.UtcNow
200
+ });
201
+ }
202
+
203
  return BadRequest(new PaymentResultDTO
204
  {
205
  Success = false,
 
211
  });
212
  }
213
 
 
214
  var paymentRequest = new PaymentRequestDTO
215
  {
216
  Amount = totalAmount,
217
  Currency = "UAH",
218
  OrderId = order.Id,
219
+ PaymentMethod = paymentDto.PaymentMethod,
220
+ StripeCardToken = paymentDto.StripeCardToken
221
  };
222
 
223
  var paymentResult = await _paymentProcessorService.ProcessPaymentAsync(paymentRequest);
 
237
  if (willPayProduct)
238
  {
239
  order.IsProductPaid = true;
240
+ // Add ProductPrice to Sender's balance
241
+ var sender = await _userRepository.GetByIdAsync(order.SenderId);
242
+ if (sender != null)
243
+ {
244
+ sender.Balance += order.ProductPrice;
245
+ await _userRepository.UpdateAsync(sender);
246
+ }
247
  }
248
  if (willPayDelivery)
249
  {
250
  order.IsDeliveryPaid = true;
251
  }
252
 
253
+ // Update order status
254
+ if (order.Status == OrderStatus.AwaitingPayment && order.IsDeliveryPaid)
255
+ {
256
+ order.Status = OrderStatus.AwaitingConfirmation;
257
+ }
258
+ if (order.Status == OrderStatus.AwaitingConfirmation && order.IsProductPaid && order.IsDeliveryPaid)
259
+ {
260
+ order.Status = OrderStatus.Pending;
261
+ }
262
+
263
  await _orderRepository.UpdateAsync(order);
264
 
265
  // Return success result
RobDeliveryAPI/Controllers/WalletController.cs ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Application.Abstractions.Interfaces;
2
+ using Entities.Interfaces;
3
+ using Microsoft.AspNetCore.Authorization;
4
+ using Microsoft.AspNetCore.Mvc;
5
+ using System.Security.Claims;
6
+
7
+ namespace RobDeliveryAPI.Controllers;
8
+
9
+ [Route("api/[controller]")]
10
+ [ApiController]
11
+ [Authorize]
12
+ public class WalletController : ControllerBase
13
+ {
14
+ private readonly IUserRepository _userRepository;
15
+
16
+ public WalletController(IUserRepository userRepository)
17
+ {
18
+ _userRepository = userRepository;
19
+ }
20
+
21
+ private int GetAuthenticatedUserId()
22
+ {
23
+ var userIdClaim = User.FindFirst("Id") ?? User.FindFirst(ClaimTypes.NameIdentifier);
24
+ if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out int userId))
25
+ {
26
+ throw new UnauthorizedAccessException("User ID not found in token");
27
+ }
28
+ return userId;
29
+ }
30
+
31
+ [HttpGet("balance")]
32
+ public async Task<ActionResult<object>> GetBalance()
33
+ {
34
+ try
35
+ {
36
+ int userId = GetAuthenticatedUserId();
37
+ var user = await _userRepository.GetByIdAsync(userId);
38
+
39
+ if (user == null)
40
+ {
41
+ return NotFound(new { success = false, message = "User not found" });
42
+ }
43
+
44
+ return Ok(new { success = true, balance = user.Balance });
45
+ }
46
+ catch (UnauthorizedAccessException ex)
47
+ {
48
+ return StatusCode(403, new { success = false, message = ex.Message });
49
+ }
50
+ catch (Exception ex)
51
+ {
52
+ return StatusCode(500, new { success = false, message = $"Internal server error: {ex.Message}" });
53
+ }
54
+ }
55
+
56
+ [HttpPost("withdraw")]
57
+ public async Task<ActionResult<object>> Withdraw()
58
+ {
59
+ try
60
+ {
61
+ int userId = GetAuthenticatedUserId();
62
+ var user = await _userRepository.GetByIdAsync(userId);
63
+
64
+ if (user == null)
65
+ {
66
+ return NotFound(new { success = false, message = "User not found" });
67
+ }
68
+
69
+ if (user.Balance <= 0)
70
+ {
71
+ return BadRequest(new { success = false, message = "No funds to withdraw" });
72
+ }
73
+
74
+ decimal withdrawnAmount = user.Balance;
75
+ // Simulate Payout - just reset to 0
76
+ user.Balance = 0;
77
+ await _userRepository.UpdateAsync(user);
78
+
79
+ return Ok(new
80
+ {
81
+ success = true,
82
+ message = "Withdrawal successful",
83
+ withdrawnAmount = withdrawnAmount,
84
+ newBalance = user.Balance,
85
+ processedAt = DateTime.UtcNow
86
+ });
87
+ }
88
+ catch (UnauthorizedAccessException ex)
89
+ {
90
+ return StatusCode(403, new { success = false, message = ex.Message });
91
+ }
92
+ catch (Exception ex)
93
+ {
94
+ return StatusCode(500, new { success = false, message = $"Internal server error: {ex.Message}" });
95
+ }
96
+ }
97
+ }
RobDeliveryAPI/appsettings.json CHANGED
@@ -31,8 +31,8 @@
31
  "Mode": "sandbox"
32
  },
33
  "Stripe": {
34
- "SecretKey": "sk_test_YOUR_STRIPE_SECRET_KEY",
35
- "PublishableKey": "pk_test_YOUR_STRIPE_PUBLISHABLE_KEY",
36
  "WebhookSecret": "whsec_YOUR_WEBHOOK_SECRET"
37
  },
38
  "GooglePay": {
 
31
  "Mode": "sandbox"
32
  },
33
  "Stripe": {
34
+ "SecretKey": "sk_test_51SaF97AXzGI3BHqbQQ46nn8L4gSByN4FnGuzuAlOPhLxNPhtC00dLrx1gqeZq5j0LyPmRGBEWJSFVao6GaMRh7ak00n2cWO4fV",
35
+ "PublishableKey": "pk_test_51SaF97AXzGI3BHqbx7tGtlNcbB2MEU4SNLqFcE0fgjf6S8QjyDRIpyhMuw27ytANFCNV7PtkcOVQZ4NwJGwkHmtO00FUQtAwwN",
36
  "WebhookSecret": "whsec_YOUR_WEBHOOK_SECRET"
37
  },
38
  "GooglePay": {