更新20260316

This commit is contained in:
GukSang.Jin
2026-03-16 15:38:08 +08:00
parent 5b7238befa
commit 54b3448e31
16 changed files with 1132 additions and 30 deletions

View File

@@ -9,10 +9,12 @@ namespace PetWash.Api.Controllers;
public class OrdersController : ControllerBase
{
private readonly OrderService _orderService;
private readonly WeChatPayService _weChatPayService;
public OrdersController(OrderService orderService)
public OrdersController(OrderService orderService, WeChatPayService weChatPayService)
{
_orderService = orderService;
_weChatPayService = weChatPayService;
}
[HttpPost]
@@ -21,12 +23,24 @@ public class OrdersController : ControllerBase
try
{
var order = await _orderService.CreateOrderAsync(request.PackageId);
return Ok(order);
var payment = await _weChatPayService.CreateNativePayAsync(order, HttpContext.RequestAborted);
return Ok(new CreateOrderResponse
{
Order = order,
CodeUrl = payment.CodeUrl,
OutTradeNo = payment.OutTradeNo,
ExpiresAt = payment.ExpiresAt
});
}
catch (ArgumentException ex)
{
return BadRequest(ex.Message);
}
catch (InvalidOperationException ex)
{
return StatusCode(StatusCodes.Status502BadGateway, ex.Message);
}
}
[HttpPost("{id}/payment")]
@@ -39,6 +53,56 @@ public class OrdersController : ControllerBase
return Ok(order);
}
[HttpGet("{id}/payment-status")]
public async Task<IActionResult> GetPaymentStatus(int id, [FromQuery] string outTradeNo)
{
if (string.IsNullOrWhiteSpace(outTradeNo))
{
return BadRequest("outTradeNo is required.");
}
var order = await _orderService.GetOrderAsync(id);
if (order == null)
{
return NotFound();
}
if (order.IsPaid)
{
return Ok(new PaymentStatusResponse
{
Order = order,
IsPaid = true,
TradeState = "SUCCESS",
OutTradeNo = outTradeNo,
Message = "Order already confirmed."
});
}
try
{
var payment = await _weChatPayService.QueryOrderByOutTradeNoAsync(outTradeNo, HttpContext.RequestAborted);
if (string.Equals(payment.TradeState, "SUCCESS", StringComparison.OrdinalIgnoreCase))
{
order = await _orderService.ConfirmPaymentAsync(id) ?? order;
}
return Ok(new PaymentStatusResponse
{
Order = order,
IsPaid = order.IsPaid,
TradeState = payment.TradeState,
OutTradeNo = payment.OutTradeNo,
TransactionId = payment.TransactionId,
Message = order.IsPaid ? "Payment confirmed." : "Waiting for payment."
});
}
catch (InvalidOperationException ex)
{
return StatusCode(StatusCodes.Status502BadGateway, ex.Message);
}
}
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(int id)
{

View File

@@ -0,0 +1,57 @@
using Microsoft.AspNetCore.Mvc;
using PetWash.Api.Services;
namespace PetWash.Api.Controllers;
[ApiController]
[Route("api/payments/wechat")]
public class PaymentsController : ControllerBase
{
private readonly OrderService _orderService;
private readonly WeChatPayService _weChatPayService;
private readonly ILogger<PaymentsController> _logger;
public PaymentsController(
OrderService orderService,
WeChatPayService weChatPayService,
ILogger<PaymentsController> logger)
{
_orderService = orderService;
_weChatPayService = weChatPayService;
_logger = logger;
}
[HttpPost("notify")]
public async Task<IActionResult> Notify(CancellationToken cancellationToken)
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync(cancellationToken);
try
{
var notification = await _weChatPayService.ParsePaymentNotificationAsync(
body,
Request.Headers,
cancellationToken);
if (string.Equals(notification.TradeState, "SUCCESS", StringComparison.OrdinalIgnoreCase))
{
var order = await _orderService.ConfirmPaymentAsync(notification.OrderId);
if (order == null)
{
_logger.LogWarning(
"WeChat payment notification references a missing order. OrderId={OrderId}, OutTradeNo={OutTradeNo}",
notification.OrderId,
notification.OutTradeNo);
}
}
return Ok(new { code = "SUCCESS", message = "成功" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to process WeChat payment notification.");
return BadRequest(new { code = "FAIL", message = ex.Message });
}
}
}