♻️ Finish centerlizing the data models
This commit is contained in:
@@ -25,7 +25,7 @@ public class OrderController(PaymentService payment, AuthService auth, AppDataba
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnWalletOrder>> PayOrder(Guid id, [FromBody] PayOrderRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
// Validate PIN code
|
||||
if (!await auth.ValidatePinCode(currentUser.Id, request.PinCode))
|
||||
|
@@ -2,7 +2,6 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Wallet.PaymentHandlers;
|
||||
|
@@ -1,14 +1,11 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Stream;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Client.JetStream;
|
||||
using NATS.Net;
|
||||
using NodaTime;
|
||||
using AccountService = DysonNetwork.Pass.Account.AccountService;
|
||||
@@ -23,7 +20,7 @@ public class PaymentService(
|
||||
INatsConnection nats
|
||||
)
|
||||
{
|
||||
public async Task<Order> CreateOrderAsync(
|
||||
public async Task<SnWalletOrder> CreateOrderAsync(
|
||||
Guid? payeeWalletId,
|
||||
string currency,
|
||||
decimal amount,
|
||||
@@ -36,11 +33,11 @@ public class PaymentService(
|
||||
)
|
||||
{
|
||||
// Check if there's an existing unpaid order that can be reused
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
if (reuseable && appIdentifier != null)
|
||||
{
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var existingOrder = await db.PaymentOrders
|
||||
.Where(o => o.Status == OrderStatus.Unpaid &&
|
||||
.Where(o => o.Status == Shared.Models.OrderStatus.Unpaid &&
|
||||
o.PayeeWalletId == payeeWalletId &&
|
||||
o.Currency == currency &&
|
||||
o.Amount == amount &&
|
||||
@@ -60,13 +57,13 @@ public class PaymentService(
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new order if no reusable order was found
|
||||
var order = new Order
|
||||
// Create a new SnWalletOrder if no reusable order was found
|
||||
var order = new SnWalletOrder
|
||||
{
|
||||
PayeeWalletId = payeeWalletId,
|
||||
Currency = currency,
|
||||
Amount = amount,
|
||||
ExpiredAt = SystemClock.Instance.GetCurrentInstant().Plus(expiration ?? Duration.FromHours(24)),
|
||||
ExpiredAt = now.Plus(expiration ?? Duration.FromHours(24)),
|
||||
AppIdentifier = appIdentifier,
|
||||
ProductIdentifier = productIdentifier,
|
||||
Remarks = remarks,
|
||||
@@ -78,16 +75,16 @@ public class PaymentService(
|
||||
return order;
|
||||
}
|
||||
|
||||
public async Task<Transaction> CreateTransactionWithAccountAsync(
|
||||
public async Task<SnWalletTransaction> CreateTransactionWithAccountAsync(
|
||||
Guid? payerAccountId,
|
||||
Guid? payeeAccountId,
|
||||
string currency,
|
||||
decimal amount,
|
||||
string? remarks = null,
|
||||
TransactionType type = TransactionType.System
|
||||
Shared.Models.TransactionType type = Shared.Models.TransactionType.System
|
||||
)
|
||||
{
|
||||
Wallet? payer = null, payee = null;
|
||||
SnWallet? payer = null, payee = null;
|
||||
if (payerAccountId.HasValue)
|
||||
payer = await db.Wallets.FirstOrDefaultAsync(e => e.AccountId == payerAccountId.Value);
|
||||
if (payeeAccountId.HasValue)
|
||||
@@ -108,13 +105,13 @@ public class PaymentService(
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<Transaction> CreateTransactionAsync(
|
||||
public async Task<SnWalletTransaction> CreateTransactionAsync(
|
||||
Guid? payerWalletId,
|
||||
Guid? payeeWalletId,
|
||||
string currency,
|
||||
decimal amount,
|
||||
string? remarks = null,
|
||||
TransactionType type = TransactionType.System,
|
||||
Shared.Models.TransactionType type = Shared.Models.TransactionType.System,
|
||||
bool silent = false
|
||||
)
|
||||
{
|
||||
@@ -122,7 +119,7 @@ public class PaymentService(
|
||||
throw new ArgumentException("At least one wallet must be specified.");
|
||||
if (amount <= 0) throw new ArgumentException("Cannot create transaction with negative or zero amount.");
|
||||
|
||||
var transaction = new Transaction
|
||||
var transaction = new SnWalletTransaction
|
||||
{
|
||||
PayerWalletId = payerWalletId,
|
||||
PayeeWalletId = payeeWalletId,
|
||||
@@ -132,7 +129,7 @@ public class PaymentService(
|
||||
Type = type
|
||||
};
|
||||
|
||||
Wallet? payerWallet = null, payeeWallet = null;
|
||||
SnWallet? payerWallet = null, payeeWallet = null;
|
||||
|
||||
if (payerWalletId.HasValue)
|
||||
{
|
||||
@@ -173,7 +170,7 @@ public class PaymentService(
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private async Task NotifyNewTransaction(Transaction transaction, Wallet? payerWallet, Wallet? payeeWallet)
|
||||
private async Task NotifyNewTransaction(SnWalletTransaction transaction, SnWallet? payerWallet, SnWallet? payeeWallet)
|
||||
{
|
||||
if (payerWallet is not null)
|
||||
{
|
||||
@@ -244,21 +241,15 @@ public class PaymentService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Order> PayOrderAsync(Guid orderId, Wallet payerWallet)
|
||||
public async Task<SnWalletOrder> PayOrderAsync(Guid orderId, SnWallet payerWallet)
|
||||
{
|
||||
var order = await db.PaymentOrders
|
||||
.Include(o => o.Transaction)
|
||||
.Include(o => o.PayeeWallet)
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId);
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
throw new InvalidOperationException("Order not found");
|
||||
}
|
||||
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId) ?? throw new InvalidOperationException("Order not found");
|
||||
var js = nats.CreateJetStreamContext();
|
||||
|
||||
if (order.Status == OrderStatus.Paid)
|
||||
if (order.Status == Shared.Models.OrderStatus.Paid)
|
||||
{
|
||||
await js.PublishAsync(
|
||||
PaymentOrderEventBase.Type,
|
||||
@@ -277,14 +268,14 @@ public class PaymentService(
|
||||
return order;
|
||||
}
|
||||
|
||||
if (order.Status != OrderStatus.Unpaid)
|
||||
if (order.Status != Shared.Models.OrderStatus.Unpaid)
|
||||
{
|
||||
throw new InvalidOperationException($"Order is in invalid status: {order.Status}");
|
||||
}
|
||||
|
||||
if (order.ExpiredAt < SystemClock.Instance.GetCurrentInstant())
|
||||
{
|
||||
order.Status = OrderStatus.Expired;
|
||||
order.Status = Shared.Models.OrderStatus.Expired;
|
||||
await db.SaveChangesAsync();
|
||||
throw new InvalidOperationException("Order has expired");
|
||||
}
|
||||
@@ -295,12 +286,12 @@ public class PaymentService(
|
||||
order.Currency,
|
||||
order.Amount,
|
||||
order.Remarks ?? $"Payment for Order #{order.Id}",
|
||||
type: TransactionType.Order,
|
||||
type: Shared.Models.TransactionType.Order,
|
||||
silent: true);
|
||||
|
||||
order.TransactionId = transaction.Id;
|
||||
order.Transaction = transaction;
|
||||
order.Status = OrderStatus.Paid;
|
||||
order.Status = Shared.Models.OrderStatus.Paid;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
@@ -323,7 +314,7 @@ public class PaymentService(
|
||||
return order;
|
||||
}
|
||||
|
||||
private async Task NotifyOrderPaid(Order order, Wallet? payerWallet, Wallet? payeeWallet)
|
||||
private async Task NotifyOrderPaid(SnWalletOrder order, SnWallet? payerWallet, SnWallet? payeeWallet)
|
||||
{
|
||||
if (payerWallet is not null)
|
||||
{
|
||||
@@ -387,7 +378,7 @@ public class PaymentService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Order> CancelOrderAsync(Guid orderId)
|
||||
public async Task<SnWalletOrder> CancelOrderAsync(Guid orderId)
|
||||
{
|
||||
var order = await db.PaymentOrders.FindAsync(orderId);
|
||||
if (order == null)
|
||||
@@ -395,28 +386,22 @@ public class PaymentService(
|
||||
throw new InvalidOperationException("Order not found");
|
||||
}
|
||||
|
||||
if (order.Status != OrderStatus.Unpaid)
|
||||
if (order.Status != Shared.Models.OrderStatus.Unpaid)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot cancel order in status: {order.Status}");
|
||||
}
|
||||
|
||||
order.Status = OrderStatus.Cancelled;
|
||||
order.Status = Shared.Models.OrderStatus.Cancelled;
|
||||
await db.SaveChangesAsync();
|
||||
return order;
|
||||
}
|
||||
|
||||
public async Task<(Order Order, Transaction RefundTransaction)> RefundOrderAsync(Guid orderId)
|
||||
public async Task<(SnWalletOrder Order, SnWalletTransaction RefundTransaction)> RefundOrderAsync(Guid orderId)
|
||||
{
|
||||
var order = await db.PaymentOrders
|
||||
.Include(o => o.Transaction)
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId);
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
throw new InvalidOperationException("Order not found");
|
||||
}
|
||||
|
||||
if (order.Status != OrderStatus.Paid)
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId) ?? throw new InvalidOperationException("Order not found");
|
||||
if (order.Status != Shared.Models.OrderStatus.Paid)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot refund order in status: {order.Status}");
|
||||
}
|
||||
@@ -433,13 +418,13 @@ public class PaymentService(
|
||||
order.Amount,
|
||||
$"Refund for order {order.Id}");
|
||||
|
||||
order.Status = OrderStatus.Finished;
|
||||
order.Status = Shared.Models.OrderStatus.Finished;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return (order, refundTransaction);
|
||||
}
|
||||
|
||||
public async Task<Transaction> TransferAsync(Guid payerAccountId, Guid payeeAccountId, string currency,
|
||||
public async Task<SnWalletTransaction> TransferAsync(Guid payerAccountId, Guid payeeAccountId, string currency,
|
||||
decimal amount)
|
||||
{
|
||||
var payerWallet = await wat.GetWalletAsync(payerAccountId);
|
||||
@@ -460,6 +445,6 @@ public class PaymentService(
|
||||
currency,
|
||||
amount,
|
||||
$"Transfer from account {payerAccountId} to {payeeAccountId}",
|
||||
TransactionType.Transfer);
|
||||
Shared.Models.TransactionType.Transfer);
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@ public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.Pa
|
||||
request.Currency,
|
||||
decimal.Parse(request.Amount),
|
||||
request.Expiration is not null ? Duration.FromSeconds(request.Expiration.Seconds) : null,
|
||||
request.HasAppIdentifier ? request.AppIdentifier : Order.InternalAppIdentifier,
|
||||
request.HasAppIdentifier ? request.AppIdentifier : SnWalletOrder.InternalAppIdentifier,
|
||||
request.HasProductIdentifier ? request.ProductIdentifier : null,
|
||||
request.HasRemarks ? request.Remarks : null,
|
||||
request.HasMeta
|
||||
@@ -34,7 +34,7 @@ public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.Pa
|
||||
request.Currency,
|
||||
decimal.Parse(request.Amount),
|
||||
request.HasRemarks ? request.Remarks : null,
|
||||
(TransactionType)request.Type
|
||||
(Shared.Models.TransactionType)request.Type
|
||||
);
|
||||
return transaction.ToProtoValue();
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.Pa
|
||||
request.Currency,
|
||||
decimal.Parse(request.Amount),
|
||||
request.HasRemarks ? request.Remarks : null,
|
||||
(TransactionType)request.Type
|
||||
(Shared.Models.TransactionType)request.Type
|
||||
);
|
||||
return transaction.ToProtoValue();
|
||||
}
|
||||
|
@@ -15,12 +15,12 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
{
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<SnSubscription>>> ListSubscriptions(
|
||||
public async Task<ActionResult<List<SnWalletSubscription>>> ListSubscriptions(
|
||||
[FromQuery] int offset = 0,
|
||||
[FromQuery] int take = 20
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var query = db.WalletSubscriptions.AsQueryable()
|
||||
.Where(s => s.AccountId == currentUser.Id)
|
||||
@@ -41,9 +41,9 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
|
||||
[HttpGet("fuzzy/{prefix}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnSubscription>> GetSubscriptionFuzzy(string prefix)
|
||||
public async Task<ActionResult<SnWalletSubscription>> GetSubscriptionFuzzy(string prefix)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var subscription = await db.WalletSubscriptions
|
||||
.Where(s => s.AccountId == currentUser.Id && s.IsActive)
|
||||
@@ -57,9 +57,9 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
|
||||
[HttpGet("{identifier}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnSubscription>> GetSubscription(string identifier)
|
||||
public async Task<ActionResult<SnWalletSubscription>> GetSubscription(string identifier)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var subscription = await subscriptions.GetSubscriptionAsync(currentUser.Id, identifier);
|
||||
if (subscription is null) return NotFound($"Subscription with identifier {identifier} was not found.");
|
||||
@@ -71,7 +71,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
{
|
||||
[Required] public string Identifier { get; set; } = null!;
|
||||
[Required] public string PaymentMethod { get; set; } = null!;
|
||||
[Required] public PaymentDetails PaymentDetails { get; set; } = null!;
|
||||
[Required] public SnPaymentDetails PaymentDetails { get; set; } = null!;
|
||||
public string? Coupon { get; set; }
|
||||
public int? CycleDurationDays { get; set; }
|
||||
public bool IsFreeTrial { get; set; } = false;
|
||||
@@ -80,12 +80,12 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnSubscription>> CreateSubscription(
|
||||
public async Task<ActionResult<SnWalletSubscription>> CreateSubscription(
|
||||
[FromBody] CreateSubscriptionRequest request,
|
||||
[FromHeader(Name = "X-Noop")] bool noop = false
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
Duration? cycleDuration = null;
|
||||
if (request.CycleDurationDays.HasValue)
|
||||
@@ -119,9 +119,9 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
|
||||
[HttpPost("{identifier}/cancel")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnSubscription>> CancelSubscription(string identifier)
|
||||
public async Task<ActionResult<SnWalletSubscription>> CancelSubscription(string identifier)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -138,7 +138,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnWalletOrder>> CreateSubscriptionOrder(string identifier)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
|
@@ -6,11 +6,9 @@ using DysonNetwork.Pass.Wallet.PaymentHandlers;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
using AccountService = DysonNetwork.Pass.Account.AccountService;
|
||||
using Duration = NodaTime.Duration;
|
||||
|
||||
namespace DysonNetwork.Pass.Wallet;
|
||||
@@ -18,7 +16,7 @@ namespace DysonNetwork.Pass.Wallet;
|
||||
public class SubscriptionService(
|
||||
AppDatabase db,
|
||||
PaymentService payment,
|
||||
AccountService accounts,
|
||||
Account.AccountService accounts,
|
||||
RingService.RingServiceClient pusher,
|
||||
IStringLocalizer<NotificationResource> localizer,
|
||||
IConfiguration configuration,
|
||||
@@ -26,11 +24,11 @@ public class SubscriptionService(
|
||||
ILogger<SubscriptionService> logger
|
||||
)
|
||||
{
|
||||
public async Task<Subscription> CreateSubscriptionAsync(
|
||||
Account.Account account,
|
||||
public async Task<SnWalletSubscription> CreateSubscriptionAsync(
|
||||
SnAccount account,
|
||||
string identifier,
|
||||
string paymentMethod,
|
||||
PaymentDetails paymentDetails,
|
||||
SnPaymentDetails paymentDetails,
|
||||
Duration? cycleDuration = null,
|
||||
string? coupon = null,
|
||||
bool isFreeTrial = false,
|
||||
@@ -80,7 +78,7 @@ public class SubscriptionService(
|
||||
throw new InvalidOperationException("Free trial already exists.");
|
||||
}
|
||||
|
||||
Coupon? couponData = null;
|
||||
SnWalletCoupon? couponData = null;
|
||||
if (coupon is not null)
|
||||
{
|
||||
var inputCouponId = Guid.TryParse(coupon, out var parsedCouponId) ? parsedCouponId : Guid.Empty;
|
||||
@@ -91,14 +89,14 @@ public class SubscriptionService(
|
||||
}
|
||||
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var subscription = new Subscription
|
||||
var subscription = new SnWalletSubscription
|
||||
{
|
||||
BegunAt = now,
|
||||
EndedAt = now.Plus(cycleDuration.Value),
|
||||
Identifier = identifier,
|
||||
IsActive = true,
|
||||
IsFreeTrial = isFreeTrial,
|
||||
Status = SubscriptionStatus.Unpaid,
|
||||
Status = Shared.Models.SubscriptionStatus.Unpaid,
|
||||
PaymentMethod = paymentMethod,
|
||||
PaymentDetails = paymentDetails,
|
||||
BasePrice = subscriptionInfo.BasePrice,
|
||||
@@ -114,7 +112,7 @@ public class SubscriptionService(
|
||||
return subscription;
|
||||
}
|
||||
|
||||
public async Task<Subscription> CreateSubscriptionFromOrder(ISubscriptionOrder order)
|
||||
public async Task<SnWalletSubscription> CreateSubscriptionFromOrder(ISubscriptionOrder order)
|
||||
{
|
||||
var cfgSection = configuration.GetSection("Payment:Subscriptions");
|
||||
var provider = order.Provider;
|
||||
@@ -141,7 +139,7 @@ public class SubscriptionService(
|
||||
throw new ArgumentOutOfRangeException(nameof(subscriptionIdentifier),
|
||||
$@"Subscription {subscriptionIdentifier} was not found.");
|
||||
|
||||
Account.Account? account = null;
|
||||
SnAccount? account = null;
|
||||
if (!string.IsNullOrEmpty(provider))
|
||||
account = await accounts.LookupAccountByConnection(order.AccountId, provider);
|
||||
else if (Guid.TryParse(order.AccountId, out var accountId))
|
||||
@@ -164,7 +162,7 @@ public class SubscriptionService(
|
||||
existingSubscription.PaymentDetails.OrderId = order.Id;
|
||||
existingSubscription.EndedAt = order.BegunAt.Plus(cycleDuration);
|
||||
existingSubscription.RenewalAt = order.BegunAt.Plus(cycleDuration);
|
||||
existingSubscription.Status = SubscriptionStatus.Active;
|
||||
existingSubscription.Status = Shared.Models.SubscriptionStatus.Active;
|
||||
|
||||
db.Update(existingSubscription);
|
||||
await db.SaveChangesAsync();
|
||||
@@ -172,15 +170,15 @@ public class SubscriptionService(
|
||||
return existingSubscription;
|
||||
}
|
||||
|
||||
var subscription = new Subscription
|
||||
var subscription = new SnWalletSubscription
|
||||
{
|
||||
BegunAt = order.BegunAt,
|
||||
EndedAt = order.BegunAt.Plus(cycleDuration),
|
||||
IsActive = true,
|
||||
Status = SubscriptionStatus.Active,
|
||||
Status = Shared.Models.SubscriptionStatus.Active,
|
||||
Identifier = subscriptionIdentifier,
|
||||
PaymentMethod = provider,
|
||||
PaymentDetails = new PaymentDetails
|
||||
PaymentDetails = new Shared.Models.SnPaymentDetails
|
||||
{
|
||||
Currency = currency,
|
||||
OrderId = order.Id,
|
||||
@@ -205,12 +203,12 @@ public class SubscriptionService(
|
||||
/// <param name="identifier">The subscription identifier</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException">The active subscription was not found</exception>
|
||||
public async Task<Subscription> CancelSubscriptionAsync(Guid accountId, string identifier)
|
||||
public async Task<SnWalletSubscription> CancelSubscriptionAsync(Guid accountId, string identifier)
|
||||
{
|
||||
var subscription = await GetSubscriptionAsync(accountId, identifier);
|
||||
if (subscription is null)
|
||||
throw new InvalidOperationException($"Subscription with identifier {identifier} was not found.");
|
||||
if (subscription.Status != SubscriptionStatus.Active)
|
||||
if (subscription.Status != Shared.Models.SubscriptionStatus.Active)
|
||||
throw new InvalidOperationException("Subscription is already cancelled.");
|
||||
if (subscription.RenewalAt is null)
|
||||
throw new InvalidOperationException("Subscription is no need to be cancelled.");
|
||||
@@ -238,11 +236,11 @@ public class SubscriptionService(
|
||||
/// <param name="identifier">The unique subscription identifier.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the created subscription order.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when no matching unpaid or expired subscription is found.</exception>
|
||||
public async Task<Order> CreateSubscriptionOrder(Guid accountId, string identifier)
|
||||
public async Task<SnWalletOrder> CreateSubscriptionOrder(Guid accountId, string identifier)
|
||||
{
|
||||
var subscription = await db.WalletSubscriptions
|
||||
.Where(s => s.AccountId == accountId && s.Identifier == identifier)
|
||||
.Where(s => s.Status != SubscriptionStatus.Expired)
|
||||
.Where(s => s.Status != Shared.Models.SubscriptionStatus.Expired)
|
||||
.Include(s => s.Coupon)
|
||||
.OrderByDescending(s => s.BegunAt)
|
||||
.FirstOrDefaultAsync();
|
||||
@@ -268,9 +266,9 @@ public class SubscriptionService(
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<Subscription> HandleSubscriptionOrder(Order order)
|
||||
public async Task<SnWalletSubscription> HandleSubscriptionOrder(SnWalletOrder order)
|
||||
{
|
||||
if (order.Status != OrderStatus.Paid || order.Meta?["subscription_id"] is not JsonElement subscriptionIdJson)
|
||||
if (order.Status != Shared.Models.OrderStatus.Paid || order.Meta?["subscription_id"] is not JsonElement subscriptionIdJson)
|
||||
throw new InvalidOperationException("Invalid order.");
|
||||
|
||||
var subscriptionId = Guid.TryParse(subscriptionIdJson.ToString(), out var parsedSubscriptionId)
|
||||
@@ -285,7 +283,7 @@ public class SubscriptionService(
|
||||
if (subscription is null)
|
||||
throw new InvalidOperationException("Invalid order.");
|
||||
|
||||
if (subscription.Status == SubscriptionStatus.Expired)
|
||||
if (subscription.Status == Shared.Models.SubscriptionStatus.Expired)
|
||||
{
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var cycle = subscription.BegunAt.Minus(subscription.RenewalAt ?? subscription.EndedAt ?? now);
|
||||
@@ -297,7 +295,7 @@ public class SubscriptionService(
|
||||
subscription.EndedAt = nextEndedAt;
|
||||
}
|
||||
|
||||
subscription.Status = SubscriptionStatus.Active;
|
||||
subscription.Status = Shared.Models.SubscriptionStatus.Active;
|
||||
|
||||
db.Update(subscription);
|
||||
await db.SaveChangesAsync();
|
||||
@@ -320,7 +318,7 @@ public class SubscriptionService(
|
||||
// Find active subscriptions that have passed their end date
|
||||
var expiredSubscriptions = await db.WalletSubscriptions
|
||||
.Where(s => s.IsActive)
|
||||
.Where(s => s.Status == SubscriptionStatus.Active)
|
||||
.Where(s => s.Status == Shared.Models.SubscriptionStatus.Active)
|
||||
.Where(s => s.EndedAt.HasValue && s.EndedAt.Value < now)
|
||||
.Take(batchSize)
|
||||
.ToListAsync();
|
||||
@@ -330,7 +328,7 @@ public class SubscriptionService(
|
||||
|
||||
foreach (var subscription in expiredSubscriptions)
|
||||
{
|
||||
subscription.Status = SubscriptionStatus.Expired;
|
||||
subscription.Status = Shared.Models.SubscriptionStatus.Expired;
|
||||
|
||||
// Clear the cache for this subscription
|
||||
var cacheKey = $"{SubscriptionCacheKeyPrefix}{subscription.AccountId}:{subscription.Identifier}";
|
||||
@@ -341,12 +339,12 @@ public class SubscriptionService(
|
||||
return expiredSubscriptions.Count;
|
||||
}
|
||||
|
||||
private async Task NotifySubscriptionBegun(Subscription subscription)
|
||||
private async Task NotifySubscriptionBegun(SnWalletSubscription subscription)
|
||||
{
|
||||
var account = await db.Accounts.FirstOrDefaultAsync(a => a.Id == subscription.AccountId);
|
||||
if (account is null) return;
|
||||
|
||||
AccountService.SetCultureInfo(account);
|
||||
Account.AccountService.SetCultureInfo(account);
|
||||
|
||||
var humanReadableName =
|
||||
SubscriptionTypeData.SubscriptionHumanReadable.TryGetValue(subscription.Identifier, out var humanReadable)
|
||||
@@ -378,7 +376,7 @@ public class SubscriptionService(
|
||||
|
||||
private const string SubscriptionCacheKeyPrefix = "subscription:";
|
||||
|
||||
public async Task<Subscription?> GetSubscriptionAsync(Guid accountId, params string[] identifiers)
|
||||
public async Task<SnWalletSubscription?> GetSubscriptionAsync(Guid accountId, params string[] identifiers)
|
||||
{
|
||||
// Create a unique cache key for this subscription
|
||||
var hashBytes = MD5.HashData(Encoding.UTF8.GetBytes(string.Join(",", identifiers)));
|
||||
@@ -387,7 +385,7 @@ public class SubscriptionService(
|
||||
var cacheKey = $"{SubscriptionCacheKeyPrefix}{accountId}:{hashIdentifier}";
|
||||
|
||||
// Try to get the subscription from cache first
|
||||
var (found, cachedSubscription) = await cache.GetAsyncWithStatus<Subscription>(cacheKey);
|
||||
var (found, cachedSubscription) = await cache.GetAsyncWithStatus<SnWalletSubscription>(cacheKey);
|
||||
if (found && cachedSubscription != null)
|
||||
{
|
||||
return cachedSubscription;
|
||||
@@ -411,12 +409,12 @@ public class SubscriptionService(
|
||||
private static readonly List<string> PerkIdentifiers =
|
||||
[SubscriptionType.Stellar, SubscriptionType.Nova, SubscriptionType.Supernova];
|
||||
|
||||
public async Task<Subscription?> GetPerkSubscriptionAsync(Guid accountId)
|
||||
public async Task<SnWalletSubscription?> GetPerkSubscriptionAsync(Guid accountId)
|
||||
{
|
||||
var cacheKey = $"{SubscriptionPerkCacheKeyPrefix}{accountId}";
|
||||
|
||||
// Try to get the subscription from cache first
|
||||
var (found, cachedSubscription) = await cache.GetAsyncWithStatus<Subscription>(cacheKey);
|
||||
var (found, cachedSubscription) = await cache.GetAsyncWithStatus<SnWalletSubscription>(cacheKey);
|
||||
if (found && cachedSubscription != null)
|
||||
{
|
||||
return cachedSubscription;
|
||||
@@ -426,7 +424,7 @@ public class SubscriptionService(
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var subscription = await db.WalletSubscriptions
|
||||
.Where(s => s.AccountId == accountId && PerkIdentifiers.Contains(s.Identifier))
|
||||
.Where(s => s.Status == SubscriptionStatus.Active)
|
||||
.Where(s => s.Status == Shared.Models.SubscriptionStatus.Active)
|
||||
.Where(s => s.EndedAt == null || s.EndedAt > now)
|
||||
.OrderByDescending(s => s.BegunAt)
|
||||
.FirstOrDefaultAsync();
|
||||
@@ -439,16 +437,16 @@ public class SubscriptionService(
|
||||
}
|
||||
|
||||
|
||||
public async Task<Dictionary<Guid, Subscription?>> GetPerkSubscriptionsAsync(List<Guid> accountIds)
|
||||
public async Task<Dictionary<Guid, SnWalletSubscription?>> GetPerkSubscriptionsAsync(List<Guid> accountIds)
|
||||
{
|
||||
var result = new Dictionary<Guid, Subscription?>();
|
||||
var result = new Dictionary<Guid, SnWalletSubscription?>();
|
||||
var missingAccountIds = new List<Guid>();
|
||||
|
||||
// Try to get the subscription from cache first
|
||||
foreach (var accountId in accountIds)
|
||||
{
|
||||
var cacheKey = $"{SubscriptionPerkCacheKeyPrefix}{accountId}";
|
||||
var (found, cachedSubscription) = await cache.GetAsyncWithStatus<Subscription>(cacheKey);
|
||||
var (found, cachedSubscription) = await cache.GetAsyncWithStatus<SnWalletSubscription>(cacheKey);
|
||||
if (found && cachedSubscription != null)
|
||||
result[accountId] = cachedSubscription;
|
||||
else
|
||||
@@ -462,7 +460,7 @@ public class SubscriptionService(
|
||||
var subscriptions = await db.WalletSubscriptions
|
||||
.Where(s => missingAccountIds.Contains(s.AccountId))
|
||||
.Where(s => PerkIdentifiers.Contains(s.Identifier))
|
||||
.Where(s => s.Status == SubscriptionStatus.Active)
|
||||
.Where(s => s.Status == Shared.Models.SubscriptionStatus.Active)
|
||||
.Where(s => s.EndedAt == null || s.EndedAt > now)
|
||||
.OrderByDescending(s => s.BegunAt)
|
||||
.ToListAsync();
|
||||
|
@@ -13,9 +13,9 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
|
||||
{
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Wallet>> CreateWallet()
|
||||
public async Task<ActionResult<SnWallet>> CreateWallet()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -30,9 +30,9 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
|
||||
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Wallet>> GetWallet()
|
||||
public async Task<ActionResult<SnWallet>> GetWallet()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var wallet = await ws.GetWalletAsync(currentUser.Id);
|
||||
if (wallet is null) return NotFound("Wallet was not found, please create one first.");
|
||||
@@ -45,7 +45,7 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
|
||||
[FromQuery] int offset = 0, [FromQuery] int take = 20
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var accountWallet = await db.Wallets.Where(w => w.AccountId == currentUser.Id).FirstOrDefaultAsync();
|
||||
if (accountWallet is null) return NotFound();
|
||||
@@ -72,7 +72,7 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
|
||||
[FromQuery] int offset = 0, [FromQuery] int take = 20
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var accountWallet = await db.Wallets.Where(w => w.AccountId == currentUser.Id).FirstOrDefaultAsync();
|
||||
if (accountWallet is null) return NotFound();
|
||||
|
@@ -5,14 +5,14 @@ namespace DysonNetwork.Pass.Wallet;
|
||||
|
||||
public class WalletService(AppDatabase db)
|
||||
{
|
||||
public async Task<Wallet?> GetWalletAsync(Guid accountId)
|
||||
public async Task<SnWallet?> GetWalletAsync(Guid accountId)
|
||||
{
|
||||
return await db.Wallets
|
||||
.Include(w => w.Pockets)
|
||||
.FirstOrDefaultAsync(w => w.AccountId == accountId);
|
||||
}
|
||||
|
||||
public async Task<Wallet> CreateWalletAsync(Guid accountId)
|
||||
public async Task<SnWallet> CreateWalletAsync(Guid accountId)
|
||||
{
|
||||
var existingWallet = await db.Wallets.FirstOrDefaultAsync(w => w.AccountId == accountId);
|
||||
if (existingWallet != null)
|
||||
@@ -20,7 +20,7 @@ public class WalletService(AppDatabase db)
|
||||
throw new InvalidOperationException($"Wallet already exists for account {accountId}");
|
||||
}
|
||||
|
||||
var wallet = new Wallet { AccountId = accountId };
|
||||
var wallet = new SnWallet { AccountId = accountId };
|
||||
|
||||
db.Wallets.Add(wallet);
|
||||
await db.SaveChangesAsync();
|
||||
|
Reference in New Issue
Block a user