✨ Payment grpc services and perks in proto
This commit is contained in:
@@ -32,7 +32,7 @@ public class Account : ModelBase
|
|||||||
|
|
||||||
[JsonIgnore] public ICollection<Relationship> OutgoingRelationships { get; set; } = new List<Relationship>();
|
[JsonIgnore] public ICollection<Relationship> OutgoingRelationships { get; set; } = new List<Relationship>();
|
||||||
[JsonIgnore] public ICollection<Relationship> IncomingRelationships { get; set; } = new List<Relationship>();
|
[JsonIgnore] public ICollection<Relationship> IncomingRelationships { get; set; } = new List<Relationship>();
|
||||||
|
|
||||||
[NotMapped] public SubscriptionReferenceObject? PerkSubscription { get; set; }
|
[NotMapped] public SubscriptionReferenceObject? PerkSubscription { get; set; }
|
||||||
|
|
||||||
public Shared.Proto.Account ToProtoValue()
|
public Shared.Proto.Account ToProtoValue()
|
||||||
@@ -46,6 +46,7 @@ public class Account : ModelBase
|
|||||||
ActivatedAt = ActivatedAt?.ToTimestamp(),
|
ActivatedAt = ActivatedAt?.ToTimestamp(),
|
||||||
IsSuperuser = IsSuperuser,
|
IsSuperuser = IsSuperuser,
|
||||||
Profile = Profile.ToProtoValue(),
|
Profile = Profile.ToProtoValue(),
|
||||||
|
PerkSubscription = PerkSubscription?.ToProtoValue(),
|
||||||
CreatedAt = CreatedAt.ToTimestamp(),
|
CreatedAt = CreatedAt.ToTimestamp(),
|
||||||
UpdatedAt = UpdatedAt.ToTimestamp()
|
UpdatedAt = UpdatedAt.ToTimestamp()
|
||||||
};
|
};
|
||||||
@@ -72,6 +73,9 @@ public class Account : ModelBase
|
|||||||
Language = proto.Language,
|
Language = proto.Language,
|
||||||
ActivatedAt = proto.ActivatedAt?.ToInstant(),
|
ActivatedAt = proto.ActivatedAt?.ToInstant(),
|
||||||
IsSuperuser = proto.IsSuperuser,
|
IsSuperuser = proto.IsSuperuser,
|
||||||
|
PerkSubscription = proto.PerkSubscription is not null
|
||||||
|
? SubscriptionReferenceObject.FromProtoValue(proto.PerkSubscription)
|
||||||
|
: null,
|
||||||
CreatedAt = proto.CreatedAt.ToInstant(),
|
CreatedAt = proto.CreatedAt.ToInstant(),
|
||||||
UpdatedAt = proto.UpdatedAt.ToInstant(),
|
UpdatedAt = proto.UpdatedAt.ToInstant(),
|
||||||
};
|
};
|
||||||
|
@@ -39,7 +39,7 @@ public class AccountCurrentController(
|
|||||||
.Where(e => e.Id == userId)
|
.Where(e => e.Id == userId)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
var perk = await subscriptions.GetPerkSubscriptionAsync(account.Id);
|
var perk = await subscriptions.GetPerkSubscriptionAsync(account!.Id);
|
||||||
account.PerkSubscription = perk?.ToReference();
|
account.PerkSubscription = perk?.ToReference();
|
||||||
|
|
||||||
return Ok(account);
|
return Ok(account);
|
||||||
|
@@ -10,7 +10,7 @@ namespace DysonNetwork.Pass.Account;
|
|||||||
|
|
||||||
public class AccountEventService(
|
public class AccountEventService(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
PaymentService payment,
|
Wallet.PaymentService payment,
|
||||||
ICacheService cache,
|
ICacheService cache,
|
||||||
IStringLocalizer<Localization.AccountEventResource> localizer,
|
IStringLocalizer<Localization.AccountEventResource> localizer,
|
||||||
PusherService.PusherServiceClient pusher
|
PusherService.PusherServiceClient pusher
|
||||||
|
@@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
namespace DysonNetwork.Pass.Wallet;
|
namespace DysonNetwork.Pass.Wallet;
|
||||||
|
|
||||||
@@ -30,11 +31,45 @@ public class Order : ModelBase
|
|||||||
[Column(TypeName = "jsonb")] public Dictionary<string, object>? Meta { get; set; }
|
[Column(TypeName = "jsonb")] public Dictionary<string, object>? Meta { get; set; }
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public Instant ExpiredAt { get; set; }
|
public Instant ExpiredAt { get; set; }
|
||||||
|
|
||||||
public Guid? PayeeWalletId { get; set; }
|
public Guid? PayeeWalletId { get; set; }
|
||||||
public Wallet? PayeeWallet { get; set; } = null!;
|
public Wallet? PayeeWallet { get; set; } = null!;
|
||||||
public Guid? TransactionId { get; set; }
|
public Guid? TransactionId { get; set; }
|
||||||
public Transaction? Transaction { get; set; }
|
public Transaction? Transaction { get; set; }
|
||||||
|
|
||||||
|
public Shared.Proto.Order ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Status = (Shared.Proto.OrderStatus)Status,
|
||||||
|
Currency = Currency,
|
||||||
|
Remarks = Remarks,
|
||||||
|
AppIdentifier = AppIdentifier,
|
||||||
|
Meta = Meta == null
|
||||||
|
? null
|
||||||
|
: Google.Protobuf.ByteString.CopyFrom(System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(Meta)),
|
||||||
|
Amount = Amount.ToString(),
|
||||||
|
ExpiredAt = ExpiredAt.ToTimestamp(),
|
||||||
|
PayeeWalletId = PayeeWalletId?.ToString(),
|
||||||
|
TransactionId = TransactionId?.ToString(),
|
||||||
|
Transaction = Transaction?.ToProtoValue(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Order FromProtoValue(Shared.Proto.Order proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Status = (OrderStatus)proto.Status,
|
||||||
|
Currency = proto.Currency,
|
||||||
|
Remarks = proto.Remarks,
|
||||||
|
AppIdentifier = proto.AppIdentifier,
|
||||||
|
Meta = proto.HasMeta
|
||||||
|
? System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(proto.Meta.ToByteArray())
|
||||||
|
: null,
|
||||||
|
Amount = decimal.Parse(proto.Amount),
|
||||||
|
ExpiredAt = proto.ExpiredAt.ToInstant(),
|
||||||
|
PayeeWalletId = proto.HasPayeeWalletId ? Guid.Parse(proto.PayeeWalletId) : null,
|
||||||
|
TransactionId = proto.HasTransactionId ? Guid.Parse(proto.TransactionId) : null,
|
||||||
|
Transaction = proto.Transaction is not null ? Transaction.FromProtoValue(proto.Transaction) : null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TransactionType
|
public enum TransactionType
|
||||||
@@ -51,11 +86,35 @@ public class Transaction : ModelBase
|
|||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
[MaxLength(4096)] public string? Remarks { get; set; }
|
[MaxLength(4096)] public string? Remarks { get; set; }
|
||||||
public TransactionType Type { get; set; }
|
public TransactionType Type { get; set; }
|
||||||
|
|
||||||
// When the payer is null, it's pay from the system
|
// When the payer is null, it's pay from the system
|
||||||
public Guid? PayerWalletId { get; set; }
|
public Guid? PayerWalletId { get; set; }
|
||||||
|
|
||||||
public Wallet? PayerWallet { get; set; }
|
public Wallet? PayerWallet { get; set; }
|
||||||
|
|
||||||
// When the payee is null, it's pay for the system
|
// When the payee is null, it's pay for the system
|
||||||
public Guid? PayeeWalletId { get; set; }
|
public Guid? PayeeWalletId { get; set; }
|
||||||
public Wallet? PayeeWallet { get; set; }
|
public Wallet? PayeeWallet { get; set; }
|
||||||
|
|
||||||
|
public Shared.Proto.Transaction ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Currency = Currency,
|
||||||
|
Amount = Amount.ToString(),
|
||||||
|
Remarks = Remarks,
|
||||||
|
Type = (Shared.Proto.TransactionType)Type,
|
||||||
|
PayerWalletId = PayerWalletId?.ToString(),
|
||||||
|
PayeeWalletId = PayeeWalletId?.ToString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Transaction FromProtoValue(Shared.Proto.Transaction proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Currency = proto.Currency,
|
||||||
|
Amount = decimal.Parse(proto.Amount),
|
||||||
|
Remarks = proto.Remarks,
|
||||||
|
Type = (TransactionType)proto.Type,
|
||||||
|
PayerWalletId = proto.HasPayerWalletId ? Guid.Parse(proto.PayerWalletId) : null,
|
||||||
|
PayeeWalletId = proto.HasPayeeWalletId ? Guid.Parse(proto.PayeeWalletId) : null,
|
||||||
|
};
|
||||||
}
|
}
|
80
DysonNetwork.Pass/Wallet/PaymentServiceGrpc.cs
Normal file
80
DysonNetwork.Pass/Wallet/PaymentServiceGrpc.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using Grpc.Core;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Wallet;
|
||||||
|
|
||||||
|
public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.PaymentService.PaymentServiceBase
|
||||||
|
{
|
||||||
|
public override async Task<Shared.Proto.Order> CreateOrder(CreateOrderRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var order = await paymentService.CreateOrderAsync(
|
||||||
|
request.HasPayeeWalletId ? Guid.Parse(request.PayeeWalletId) : null,
|
||||||
|
request.Currency,
|
||||||
|
decimal.Parse(request.Amount),
|
||||||
|
request.Expiration is not null ? Duration.FromSeconds(request.Expiration.Seconds) : null,
|
||||||
|
request.HasAppIdentifier ? request.AppIdentifier : null,
|
||||||
|
// Assuming meta is a JSON string
|
||||||
|
request.HasMeta
|
||||||
|
? System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(request.Meta.ToStringUtf8())
|
||||||
|
: null,
|
||||||
|
request.Reuseable
|
||||||
|
);
|
||||||
|
return order.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.Transaction> CreateTransactionWithAccount(
|
||||||
|
CreateTransactionWithAccountRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var transaction = await paymentService.CreateTransactionWithAccountAsync(
|
||||||
|
request.HasPayerAccountId ? Guid.Parse(request.PayerAccountId) : null,
|
||||||
|
request.HasPayeeAccountId ? Guid.Parse(request.PayeeAccountId) : null,
|
||||||
|
request.Currency,
|
||||||
|
decimal.Parse(request.Amount),
|
||||||
|
request.HasRemarks ? request.Remarks : null,
|
||||||
|
(TransactionType)request.Type
|
||||||
|
);
|
||||||
|
return transaction.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.Transaction> CreateTransaction(CreateTransactionRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var transaction = await paymentService.CreateTransactionAsync(
|
||||||
|
request.HasPayerWalletId ? Guid.Parse(request.PayerWalletId) : null,
|
||||||
|
request.HasPayeeWalletId ? Guid.Parse(request.PayeeWalletId) : null,
|
||||||
|
request.Currency,
|
||||||
|
decimal.Parse(request.Amount),
|
||||||
|
request.HasRemarks ? request.Remarks : null,
|
||||||
|
(TransactionType)request.Type
|
||||||
|
);
|
||||||
|
return transaction.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.Order> CancelOrder(CancelOrderRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var order = await paymentService.CancelOrderAsync(Guid.Parse(request.OrderId));
|
||||||
|
return order.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<RefundOrderResponse> RefundOrder(RefundOrderRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var (order, refundTransaction) = await paymentService.RefundOrderAsync(Guid.Parse(request.OrderId));
|
||||||
|
return new RefundOrderResponse
|
||||||
|
{
|
||||||
|
Order = order.ToProtoValue(),
|
||||||
|
RefundTransaction = refundTransaction.ToProtoValue()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.Transaction> Transfer(TransferRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var transaction = await paymentService.TransferAsync(
|
||||||
|
Guid.Parse(request.PayerAccountId),
|
||||||
|
Guid.Parse(request.PayeeAccountId),
|
||||||
|
request.Currency,
|
||||||
|
decimal.Parse(request.Amount)
|
||||||
|
);
|
||||||
|
return transaction.ToProtoValue();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,10 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Globalization;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
namespace DysonNetwork.Pass.Wallet;
|
namespace DysonNetwork.Pass.Wallet;
|
||||||
|
|
||||||
@@ -199,6 +201,44 @@ public class Subscription : ModelBase
|
|||||||
AccountId = AccountId
|
AccountId = AccountId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Shared.Proto.Subscription ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
BegunAt = BegunAt.ToTimestamp(),
|
||||||
|
EndedAt = EndedAt?.ToTimestamp(),
|
||||||
|
Identifier = Identifier,
|
||||||
|
IsActive = IsActive,
|
||||||
|
IsFreeTrial = IsFreeTrial,
|
||||||
|
Status = (Shared.Proto.SubscriptionStatus)Status,
|
||||||
|
PaymentMethod = PaymentMethod,
|
||||||
|
PaymentDetails = PaymentDetails.ToProtoValue(),
|
||||||
|
BasePrice = BasePrice.ToString(),
|
||||||
|
CouponId = CouponId?.ToString(),
|
||||||
|
Coupon = Coupon?.ToProtoValue(),
|
||||||
|
RenewalAt = RenewalAt?.ToTimestamp(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
IsAvailable = IsAvailable,
|
||||||
|
FinalPrice = FinalPrice.ToString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Subscription FromProtoValue(Shared.Proto.Subscription proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
BegunAt = proto.BegunAt.ToInstant(),
|
||||||
|
EndedAt = proto.EndedAt?.ToInstant(),
|
||||||
|
Identifier = proto.Identifier,
|
||||||
|
IsActive = proto.IsActive,
|
||||||
|
IsFreeTrial = proto.IsFreeTrial,
|
||||||
|
Status = (SubscriptionStatus)proto.Status,
|
||||||
|
PaymentMethod = proto.PaymentMethod,
|
||||||
|
PaymentDetails = PaymentDetails.FromProtoValue(proto.PaymentDetails),
|
||||||
|
BasePrice = decimal.Parse(proto.BasePrice),
|
||||||
|
CouponId = proto.HasCouponId ? Guid.Parse(proto.CouponId) : null,
|
||||||
|
Coupon = proto.Coupon is not null ? Coupon.FromProtoValue(proto.Coupon) : null,
|
||||||
|
RenewalAt = proto.RenewalAt?.ToInstant(),
|
||||||
|
AccountId = Guid.Parse(proto.AccountId),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -227,12 +267,57 @@ public class SubscriptionReferenceObject : ModelBase
|
|||||||
public string? DisplayName => SubscriptionTypeData.SubscriptionHumanReadable.TryGetValue(Identifier, out var name)
|
public string? DisplayName => SubscriptionTypeData.SubscriptionHumanReadable.TryGetValue(Identifier, out var name)
|
||||||
? name
|
? name
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
public Shared.Proto.SubscriptionReferenceObject ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Identifier = Identifier,
|
||||||
|
BegunAt = BegunAt.ToTimestamp(),
|
||||||
|
EndedAt = EndedAt?.ToTimestamp(),
|
||||||
|
IsActive = IsActive,
|
||||||
|
IsAvailable = IsAvailable,
|
||||||
|
IsFreeTrial = IsFreeTrial,
|
||||||
|
Status = (Shared.Proto.SubscriptionStatus)Status,
|
||||||
|
BasePrice = BasePrice.ToString(CultureInfo.CurrentCulture),
|
||||||
|
FinalPrice = FinalPrice.ToString(CultureInfo.CurrentCulture),
|
||||||
|
RenewalAt = RenewalAt?.ToTimestamp(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
DisplayName = DisplayName,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static SubscriptionReferenceObject FromProtoValue(Shared.Proto.SubscriptionReferenceObject proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Identifier = proto.Identifier,
|
||||||
|
BegunAt = proto.BegunAt.ToInstant(),
|
||||||
|
EndedAt = proto.EndedAt?.ToInstant(),
|
||||||
|
IsActive = proto.IsActive,
|
||||||
|
IsAvailable = proto.IsAvailable,
|
||||||
|
IsFreeTrial = proto.IsFreeTrial,
|
||||||
|
Status = (SubscriptionStatus)proto.Status,
|
||||||
|
BasePrice = decimal.Parse(proto.BasePrice),
|
||||||
|
FinalPrice = decimal.Parse(proto.FinalPrice),
|
||||||
|
RenewalAt = proto.RenewalAt?.ToInstant(),
|
||||||
|
AccountId = Guid.Parse(proto.AccountId),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PaymentDetails
|
public class PaymentDetails
|
||||||
{
|
{
|
||||||
public string Currency { get; set; } = null!;
|
public string Currency { get; set; } = null!;
|
||||||
public string? OrderId { get; set; }
|
public string? OrderId { get; set; }
|
||||||
|
|
||||||
|
public Shared.Proto.PaymentDetails ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Currency = Currency,
|
||||||
|
OrderId = OrderId,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static PaymentDetails FromProtoValue(Shared.Proto.PaymentDetails proto) => new()
|
||||||
|
{
|
||||||
|
Currency = proto.Currency,
|
||||||
|
OrderId = proto.OrderId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -281,4 +366,28 @@ public class Coupon : ModelBase
|
|||||||
/// Leave it to null to use it unlimited.
|
/// Leave it to null to use it unlimited.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? MaxUsage { get; set; }
|
public int? MaxUsage { get; set; }
|
||||||
|
|
||||||
|
public Shared.Proto.Coupon ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Identifier = Identifier,
|
||||||
|
Code = Code,
|
||||||
|
AffectedAt = AffectedAt?.ToTimestamp(),
|
||||||
|
ExpiredAt = ExpiredAt?.ToTimestamp(),
|
||||||
|
DiscountAmount = DiscountAmount?.ToString(),
|
||||||
|
DiscountRate = DiscountRate,
|
||||||
|
MaxUsage = MaxUsage,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Coupon FromProtoValue(Shared.Proto.Coupon proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Identifier = proto.Identifier,
|
||||||
|
Code = proto.Code,
|
||||||
|
AffectedAt = proto.AffectedAt?.ToInstant(),
|
||||||
|
ExpiredAt = proto.ExpiredAt?.ToInstant(),
|
||||||
|
DiscountAmount = proto.HasDiscountAmount ? decimal.Parse(proto.DiscountAmount) : null,
|
||||||
|
DiscountRate = proto.DiscountRate,
|
||||||
|
MaxUsage = proto.MaxUsage,
|
||||||
|
};
|
||||||
}
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
namespace DysonNetwork.Pass.Wallet;
|
namespace DysonNetwork.Pass.Wallet;
|
||||||
|
|
||||||
@@ -12,6 +14,29 @@ public class Wallet : ModelBase
|
|||||||
|
|
||||||
public Guid AccountId { get; set; }
|
public Guid AccountId { get; set; }
|
||||||
public Account.Account Account { get; set; } = null!;
|
public Account.Account Account { get; set; } = null!;
|
||||||
|
|
||||||
|
public Shared.Proto.Wallet ToProtoValue()
|
||||||
|
{
|
||||||
|
var proto = new Shared.Proto.Wallet
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var pocket in Pockets)
|
||||||
|
{
|
||||||
|
proto.Pockets.Add(pocket.ToProtoValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Wallet FromProtoValue(Shared.Proto.Wallet proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
AccountId = Guid.Parse(proto.AccountId),
|
||||||
|
Pockets = proto.Pockets.Select(WalletPocket.FromProtoValue).ToList(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WalletPocket : ModelBase
|
public class WalletPocket : ModelBase
|
||||||
@@ -22,4 +47,20 @@ public class WalletPocket : ModelBase
|
|||||||
|
|
||||||
public Guid WalletId { get; set; }
|
public Guid WalletId { get; set; }
|
||||||
[JsonIgnore] public Wallet Wallet { get; set; } = null!;
|
[JsonIgnore] public Wallet Wallet { get; set; } = null!;
|
||||||
|
|
||||||
|
public Shared.Proto.WalletPocket ToProtoValue() => new()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Currency = Currency,
|
||||||
|
Amount = Amount.ToString(CultureInfo.CurrentCulture),
|
||||||
|
WalletId = WalletId.ToString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static WalletPocket FromProtoValue(Shared.Proto.WalletPocket proto) => new()
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Currency = proto.Currency,
|
||||||
|
Amount = decimal.Parse(proto.Amount),
|
||||||
|
WalletId = Guid.Parse(proto.WalletId),
|
||||||
|
};
|
||||||
}
|
}
|
29
DysonNetwork.Pass/Wallet/WalletServiceGrpc.cs
Normal file
29
DysonNetwork.Pass/Wallet/WalletServiceGrpc.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using Grpc.Core;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Wallet;
|
||||||
|
|
||||||
|
public class WalletServiceGrpc(WalletService walletService) : Shared.Proto.WalletService.WalletServiceBase
|
||||||
|
{
|
||||||
|
public override async Task<Shared.Proto.Wallet> GetWallet(GetWalletRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var wallet = await walletService.GetWalletAsync(Guid.Parse(request.AccountId));
|
||||||
|
if (wallet == null)
|
||||||
|
{
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "Wallet not found."));
|
||||||
|
}
|
||||||
|
return wallet.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.Wallet> CreateWallet(CreateWalletRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var wallet = await walletService.CreateWalletAsync(Guid.Parse(request.AccountId));
|
||||||
|
return wallet.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.WalletPocket> GetOrCreateWalletPocket(GetOrCreateWalletPocketRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var (pocket, _) = await walletService.GetOrCreateWalletPocketAsync(Guid.Parse(request.WalletId), request.Currency, request.HasInitialAmount ? decimal.Parse(request.InitialAmount) : null);
|
||||||
|
return pocket.ToProtoValue();
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@ import "google/protobuf/field_mask.proto";
|
|||||||
import "google/protobuf/struct.proto";
|
import "google/protobuf/struct.proto";
|
||||||
|
|
||||||
import 'file.proto';
|
import 'file.proto';
|
||||||
|
import 'wallet.proto';
|
||||||
|
|
||||||
// Account represents a user account in the system
|
// Account represents a user account in the system
|
||||||
message Account {
|
message Account {
|
||||||
@@ -21,6 +22,7 @@ message Account {
|
|||||||
bool is_superuser = 6;
|
bool is_superuser = 6;
|
||||||
|
|
||||||
AccountProfile profile = 7;
|
AccountProfile profile = 7;
|
||||||
|
optional SubscriptionReferenceObject perk_subscription = 16;
|
||||||
repeated AccountContact contacts = 8;
|
repeated AccountContact contacts = 8;
|
||||||
repeated AccountBadge badges = 9;
|
repeated AccountBadge badges = 9;
|
||||||
repeated AccountAuthFactor auth_factors = 10;
|
repeated AccountAuthFactor auth_factors = 10;
|
||||||
|
213
DysonNetwork.Shared/Proto/wallet.proto
Normal file
213
DysonNetwork.Shared/Proto/wallet.proto
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package proto;
|
||||||
|
|
||||||
|
option csharp_namespace = "DysonNetwork.Shared.Proto";
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/wrappers.proto";
|
||||||
|
import "google/protobuf/duration.proto";
|
||||||
|
|
||||||
|
message Wallet {
|
||||||
|
string id = 1;
|
||||||
|
repeated WalletPocket pockets = 2;
|
||||||
|
string account_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WalletPocket {
|
||||||
|
string id = 1;
|
||||||
|
string currency = 2;
|
||||||
|
// Using string for decimal to avoid precision loss.
|
||||||
|
string amount = 3;
|
||||||
|
string wallet_id = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SubscriptionStatus {
|
||||||
|
// Using proto3 enum naming convention
|
||||||
|
SUBSCRIPTION_STATUS_UNSPECIFIED = 0;
|
||||||
|
SUBSCRIPTION_STATUS_UNPAID = 1;
|
||||||
|
SUBSCRIPTION_STATUS_ACTIVE = 2;
|
||||||
|
SUBSCRIPTION_STATUS_EXPIRED = 3;
|
||||||
|
SUBSCRIPTION_STATUS_CANCELLED = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Subscription {
|
||||||
|
string id = 1;
|
||||||
|
google.protobuf.Timestamp begun_at = 2;
|
||||||
|
optional google.protobuf.Timestamp ended_at = 3;
|
||||||
|
string identifier = 4;
|
||||||
|
bool is_active = 5;
|
||||||
|
bool is_free_trial = 6;
|
||||||
|
SubscriptionStatus status = 7;
|
||||||
|
string payment_method = 8;
|
||||||
|
PaymentDetails payment_details = 9;
|
||||||
|
// Using string for decimal to avoid precision loss.
|
||||||
|
string base_price = 10;
|
||||||
|
optional string coupon_id = 11;
|
||||||
|
optional Coupon coupon = 12;
|
||||||
|
optional google.protobuf.Timestamp renewal_at = 13;
|
||||||
|
string account_id = 14;
|
||||||
|
bool is_available = 15;
|
||||||
|
// Using string for decimal to avoid precision loss.
|
||||||
|
string final_price = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscriptionReferenceObject {
|
||||||
|
string id = 1;
|
||||||
|
string identifier = 2;
|
||||||
|
google.protobuf.Timestamp begun_at = 3;
|
||||||
|
optional google.protobuf.Timestamp ended_at = 4;
|
||||||
|
bool is_active = 5;
|
||||||
|
bool is_available = 6;
|
||||||
|
bool is_free_trial = 7;
|
||||||
|
SubscriptionStatus status = 8;
|
||||||
|
// Using string for decimal to avoid precision loss.
|
||||||
|
string base_price = 9;
|
||||||
|
// Using string for decimal to avoid precision loss.
|
||||||
|
string final_price = 10;
|
||||||
|
optional google.protobuf.Timestamp renewal_at = 11;
|
||||||
|
string account_id = 12;
|
||||||
|
optional string display_name = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PaymentDetails {
|
||||||
|
string currency = 1;
|
||||||
|
optional string order_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Coupon {
|
||||||
|
string id = 1;
|
||||||
|
optional string identifier = 2;
|
||||||
|
optional string code = 3;
|
||||||
|
optional google.protobuf.Timestamp affected_at = 4;
|
||||||
|
optional google.protobuf.Timestamp expired_at = 5;
|
||||||
|
// Using string for decimal to avoid precision loss.
|
||||||
|
optional string discount_amount = 6;
|
||||||
|
optional google.protobuf.DoubleValue discount_rate = 7;
|
||||||
|
optional google.protobuf.Int32Value max_usage = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
service WalletService {
|
||||||
|
rpc GetWallet(GetWalletRequest) returns (Wallet);
|
||||||
|
rpc CreateWallet(CreateWalletRequest) returns (Wallet);
|
||||||
|
rpc GetOrCreateWalletPocket(GetOrCreateWalletPocketRequest) returns (WalletPocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetWalletRequest {
|
||||||
|
string account_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateWalletRequest {
|
||||||
|
string account_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetOrCreateWalletPocketRequest {
|
||||||
|
string wallet_id = 1;
|
||||||
|
string currency = 2;
|
||||||
|
optional string initial_amount = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
service PaymentService {
|
||||||
|
rpc CreateOrder(CreateOrderRequest) returns (Order);
|
||||||
|
rpc CreateTransactionWithAccount(CreateTransactionWithAccountRequest) returns (Transaction);
|
||||||
|
rpc CreateTransaction(CreateTransactionRequest) returns (Transaction);
|
||||||
|
rpc PayOrder(PayOrderRequest) returns (Order);
|
||||||
|
rpc CancelOrder(CancelOrderRequest) returns (Order);
|
||||||
|
rpc RefundOrder(RefundOrderRequest) returns (RefundOrderResponse);
|
||||||
|
rpc Transfer(TransferRequest) returns (Transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateOrderRequest {
|
||||||
|
optional string payee_wallet_id = 1;
|
||||||
|
string currency = 2;
|
||||||
|
string amount = 3;
|
||||||
|
optional google.protobuf.Duration expiration = 4;
|
||||||
|
optional string app_identifier = 5;
|
||||||
|
// Using bytes for meta to represent JSON.
|
||||||
|
optional bytes meta = 6;
|
||||||
|
bool reuseable = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Order {
|
||||||
|
string id = 1;
|
||||||
|
optional string payee_wallet_id = 2;
|
||||||
|
string currency = 3;
|
||||||
|
string amount = 4;
|
||||||
|
google.protobuf.Timestamp expired_at = 5;
|
||||||
|
optional string app_identifier = 6;
|
||||||
|
// Using bytes for meta to represent JSON.
|
||||||
|
optional bytes meta = 7;
|
||||||
|
OrderStatus status = 8;
|
||||||
|
optional string transaction_id = 9;
|
||||||
|
optional Transaction transaction = 10;
|
||||||
|
optional string remarks = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderStatus {
|
||||||
|
ORDER_STATUS_UNSPECIFIED = 0;
|
||||||
|
ORDER_STATUS_UNPAID = 1;
|
||||||
|
ORDER_STATUS_PAID = 2;
|
||||||
|
ORDER_STATUS_EXPIRED = 3;
|
||||||
|
ORDER_STATUS_CANCELLED = 4;
|
||||||
|
ORDER_STATUS_FINISHED = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Transaction {
|
||||||
|
string id = 1;
|
||||||
|
optional string payer_wallet_id = 2;
|
||||||
|
optional string payee_wallet_id = 3;
|
||||||
|
string currency = 4;
|
||||||
|
string amount = 5;
|
||||||
|
optional string remarks = 6;
|
||||||
|
TransactionType type = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransactionType {
|
||||||
|
TRANSACTION_TYPE_UNSPECIFIED = 0;
|
||||||
|
TRANSACTION_TYPE_SYSTEM = 1;
|
||||||
|
TRANSACTION_TYPE_ORDER = 2;
|
||||||
|
TRANSACTION_TYPE_TRANSFER = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateTransactionWithAccountRequest {
|
||||||
|
optional string payer_account_id = 1;
|
||||||
|
optional string payee_account_id = 2;
|
||||||
|
string currency = 3;
|
||||||
|
string amount = 4;
|
||||||
|
optional string remarks = 5;
|
||||||
|
TransactionType type = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateTransactionRequest {
|
||||||
|
optional string payer_wallet_id = 1;
|
||||||
|
optional string payee_wallet_id = 2;
|
||||||
|
string currency = 3;
|
||||||
|
string amount = 4;
|
||||||
|
optional string remarks = 5;
|
||||||
|
TransactionType type = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PayOrderRequest {
|
||||||
|
string order_id = 1;
|
||||||
|
string payer_wallet_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CancelOrderRequest {
|
||||||
|
string order_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RefundOrderRequest {
|
||||||
|
string order_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RefundOrderResponse {
|
||||||
|
Order order = 1;
|
||||||
|
Transaction refund_transaction = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransferRequest {
|
||||||
|
string payer_account_id = 1;
|
||||||
|
string payee_account_id = 2;
|
||||||
|
string currency = 3;
|
||||||
|
string amount = 4;
|
||||||
|
}
|
Reference in New Issue
Block a user