91 lines
3.4 KiB
C#
91 lines
3.4 KiB
C#
using System.Text.Json;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Tiku.Application.Commerce;
|
|
using Tiku.Domain.Commerce;
|
|
|
|
namespace Tiku.Infrastructure.Commerce;
|
|
|
|
internal sealed class CommercePaymentService(CommerceServiceDependencies dependencies)
|
|
: CommerceServiceBase(dependencies), ICommercePaymentService
|
|
{
|
|
public async Task<CommercePaymentItem> CreatePaymentAsync(
|
|
CommerceActor actor,
|
|
CreateCommercePaymentCommand command,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertActiveMemberAsync(actor, cancellationToken);
|
|
var order = await FindActorOrderAsync(actor, command.OrderNo, cancellationToken);
|
|
if (order.Status != OrderStatus.Pending)
|
|
throw new CommerceException("Only pending orders can create payments.", "order_status_invalid");
|
|
|
|
var provider = NormalizeProvider(command.Provider);
|
|
var method = NormalizeMethod(command.Method);
|
|
var payment = await commercePersistence.Payments
|
|
.Where(item =>
|
|
item.TenantId == actor.TenantId &&
|
|
item.OrderId == order.Id &&
|
|
item.Provider == provider &&
|
|
item.Status == PaymentStatus.Pending)
|
|
.OrderByDescending(item => item.CreatedAt)
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
|
|
if (payment is null)
|
|
{
|
|
payment = new Payment
|
|
{
|
|
TenantId = actor.TenantId,
|
|
OrderId = order.Id,
|
|
Provider = provider,
|
|
Method = method,
|
|
Status = PaymentStatus.Pending,
|
|
AmountCents = order.AmountCents
|
|
};
|
|
commercePersistence.Payments.Add(payment);
|
|
}
|
|
|
|
var result = await paymentGateway.CreatePaymentAsync(
|
|
provider,
|
|
new CreatePaymentProviderRequest(
|
|
actor.TenantId,
|
|
order.OrderNo,
|
|
order.ProductName ?? order.OrderNo,
|
|
order.AmountCents,
|
|
method,
|
|
command.OpenId,
|
|
command.ReturnUrl,
|
|
command.QuitUrl,
|
|
$"/api/student/commerce/payments/notify/{provider.Replace("_", "-", StringComparison.Ordinal)}?tenantId={actor.TenantId}",
|
|
JsonSerializer.SerializeToElement(new { order.Id, actor.UserId })),
|
|
cancellationToken);
|
|
|
|
payment.Method = result.Method;
|
|
payment.RawPayload = result.RawPayload;
|
|
if (!string.IsNullOrWhiteSpace(result.ProviderTradeNo)) payment.ProviderTradeNo = result.ProviderTradeNo;
|
|
|
|
if (IsPaid(result.Status))
|
|
await MarkPaidAsync(
|
|
actor,
|
|
order,
|
|
payment,
|
|
result.ProviderTradeNo,
|
|
result.RawPayload,
|
|
"payment_paid",
|
|
result.ProviderTradeNo,
|
|
true,
|
|
null,
|
|
cancellationToken);
|
|
else
|
|
commercePersistence.PaymentEvents.Add(new PaymentEvent
|
|
{
|
|
TenantId = actor.TenantId,
|
|
PaymentId = payment.Id,
|
|
Provider = provider,
|
|
EventType = "payment_created",
|
|
Payload = result.RawPayload
|
|
});
|
|
|
|
await commercePersistence.SaveChangesAsync(cancellationToken);
|
|
return ToPaymentItem(payment, order.OrderNo, result.ClientPayload);
|
|
}
|
|
}
|