Afdian as payment handler

This commit is contained in:
2025-06-23 00:29:37 +08:00
parent a23338c263
commit be0b48cfd9
8 changed files with 586 additions and 9 deletions

View File

@ -1,11 +1,19 @@
using System.Text.Json;
using DysonNetwork.Sphere.Account;
using DysonNetwork.Sphere.Storage;
using DysonNetwork.Sphere.Wallet.PaymentHandlers;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Sphere.Wallet;
public class SubscriptionService(AppDatabase db, PaymentService payment, ICacheService cache)
public class SubscriptionService(
AppDatabase db,
PaymentService payment,
AccountService accounts,
IConfiguration configuration,
ICacheService cache
)
{
public async Task<Subscription> CreateSubscriptionAsync(
Account.Account account,
@ -77,6 +85,85 @@ public class SubscriptionService(AppDatabase db, PaymentService payment, ICacheS
return subscription;
}
public async Task<Subscription> CreateSubscriptionFromOrder(ISubscriptionOrder order)
{
var cfgSection = configuration.GetSection("Payment:Subscriptions");
var provider = order.Provider;
var currency = "irl";
var subscriptionIdentifier = order.SubscriptionId;
switch (provider)
{
case "afdian":
var afdianPlans = cfgSection.GetValue<Dictionary<string, string>>("Afdian");
var afdianPlan = afdianPlans?.FirstOrDefault(p => p.Value == subscriptionIdentifier);
if (afdianPlan?.Key is not null) subscriptionIdentifier = afdianPlan.Value.Key;
currency = "cny";
break;
default:
break;
}
var subscriptionTemplate = SubscriptionTypeData
.SubscriptionDict.TryGetValue(subscriptionIdentifier, out var template)
? template
: null;
if (subscriptionTemplate is null)
throw new ArgumentOutOfRangeException(nameof(subscriptionIdentifier), $@"Subscription {subscriptionIdentifier} was not found.");
Account.Account? account = null;
if (!string.IsNullOrEmpty(provider))
account = await accounts.LookupAccountByConnection(order.AccountId, provider);
else if (Guid.TryParse(order.AccountId, out var accountId))
account = await db.Accounts.FirstOrDefaultAsync(a => a.Id == accountId);
if (account is null)
throw new InvalidOperationException($"Account was not found with identifier {order.AccountId}");
var cycleDuration = order.Duration;
var existingSubscription = await GetSubscriptionAsync(account.Id, subscriptionIdentifier);
if (existingSubscription is not null && existingSubscription.PaymentMethod != provider)
throw new InvalidOperationException($"Active subscription with identifier {subscriptionIdentifier} already exists.");
if (existingSubscription?.PaymentDetails.OrderId == order.Id)
return existingSubscription;
if (existingSubscription is not null)
{
// Same provider, but different order, renew the subscription
existingSubscription.PaymentDetails.OrderId = order.Id;
existingSubscription.EndedAt = order.BegunAt.Plus(cycleDuration);
existingSubscription.RenewalAt = order.BegunAt.Plus(cycleDuration);
existingSubscription.Status = SubscriptionStatus.Paid;
db.Update(existingSubscription);
await db.SaveChangesAsync();
return existingSubscription;
}
var subscription = new Subscription
{
BegunAt = order.BegunAt,
EndedAt = order.BegunAt.Plus(cycleDuration),
IsActive = true,
Status = SubscriptionStatus.Unpaid,
PaymentMethod = provider,
PaymentDetails = new PaymentDetails
{
Currency = currency,
OrderId = order.Id,
},
BasePrice = subscriptionTemplate.BasePrice,
RenewalAt = order.BegunAt.Plus(cycleDuration),
AccountId = account.Id,
};
db.WalletSubscriptions.Add(subscription);
await db.SaveChangesAsync();
return subscription;
}
/// <summary>
/// Cancel the renewal of the current activated subscription.
/// </summary>
@ -218,6 +305,8 @@ public class SubscriptionService(AppDatabase db, PaymentService payment, ICacheS
private const string SubscriptionCacheKeyPrefix = "subscription:";
public AccountService Accounts { get; } = accounts;
public async Task<Subscription?> GetSubscriptionAsync(Guid accountId, string identifier)
{
// Create a unique cache key for this subscription
@ -244,4 +333,4 @@ public class SubscriptionService(AppDatabase db, PaymentService payment, ICacheS
return subscription;
}
}
}