cd835ae6f9
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
- Introduced methods for initiating and verifying credit wallet charges in the WalletService. - Updated PaymentCallback.razor to handle credit wallet charge verification. - Added a button for charging the credit wallet in Wallet.razor. - Updated route constants to include the new credit wallet charge route. These changes enhance the wallet functionality by allowing users to charge their credit wallets and verify transactions seamlessly.
358 lines
11 KiB
C#
358 lines
11 KiB
C#
using DateTimeConverterCL;
|
|
using CMSMicroservice.Protobuf.Protos.UserWallet;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
|
|
namespace FrontOffice.Main.Utilities;
|
|
|
|
public record WalletBalances(long CreditBalance, long DiscountBalance, long NetworkBalance);
|
|
public record WalletTransaction(
|
|
string Date,
|
|
long CreditChange,
|
|
long CreditBalance,
|
|
long NetworkChange,
|
|
long NetworkBalance,
|
|
long DiscountChange,
|
|
long DiscountBalance,
|
|
string Channel,
|
|
string Description
|
|
);
|
|
public record WalletWithdrawal(long Id, long WeekDefinitionId, string WeekDisplayName, long Amount, int Status, int? Method, string? Iban, string Created);
|
|
public enum WithdrawalMethodClient
|
|
{
|
|
Cash = 0,
|
|
Diamond = 1
|
|
}
|
|
public record WithdrawalSettings(long MinWithdrawalAmount);
|
|
|
|
public record MagicWalletStatus(
|
|
int WalletMode,
|
|
long MagicTotalDeposited,
|
|
long MagicTotalCredited,
|
|
long MagicMaxDeposit,
|
|
long MagicRemainingDeposit,
|
|
long Balance,
|
|
DateTime? MagicActivatedAt,
|
|
int PurchaseCycleCount,
|
|
double MagicMultiplier = 2.5,
|
|
long MagicMaxCredit = 2_500_000_000
|
|
);
|
|
|
|
public class WalletService
|
|
{
|
|
private readonly CMSMicroservice.Protobuf.Protos.UserWallet.UserWalletContract.UserWalletContractClient _client;
|
|
|
|
public WalletService(CMSMicroservice.Protobuf.Protos.UserWallet.UserWalletContract.UserWalletContractClient client)
|
|
{
|
|
_client = client;
|
|
}
|
|
|
|
public async Task<WalletBalances> GetBalancesAsync()
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.GetCustomerWalletAsync(new Empty());
|
|
return new WalletBalances(response.Balance, response.DiscountBalance, response.NetworkBalance);
|
|
}
|
|
catch
|
|
{
|
|
// Return zero balances if backend is unavailable
|
|
return new WalletBalances(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
public async Task<List<WalletTransaction>> GetTransactionsAsync(long? referenceId = null, bool? isIncrease = null)
|
|
{
|
|
try
|
|
{
|
|
var request = new GetCustomerWalletHistoryRequest();
|
|
if (referenceId.HasValue)
|
|
{
|
|
request.ReferenceId = referenceId.Value;
|
|
}
|
|
if (isIncrease.HasValue)
|
|
{
|
|
request.IsIncrease = isIncrease.Value;
|
|
}
|
|
var response = await _client.GetCustomerWalletHistoryAsync(request);
|
|
|
|
return response.Models
|
|
.Select(log => new WalletTransaction(
|
|
ResolveTransactionDate(log),
|
|
log.ChangeValue,
|
|
log.CurrentBalance,
|
|
log.ChangeNerworkValue,
|
|
log.CurrentNetworkBalance,
|
|
log.ChangeDiscountValue,
|
|
log.CurrentDiscountBalance,
|
|
log.RefrenceId?.ToString() ?? "-",
|
|
ResolveDescription(log)
|
|
))
|
|
.OrderByDescending(t => t.Date)
|
|
.ToList();
|
|
}
|
|
catch
|
|
{
|
|
// Return empty list if backend is unavailable
|
|
return new List<WalletTransaction>();
|
|
}
|
|
}
|
|
|
|
private static string ResolveTransactionDate(CustomerWalletHistoryModel model)
|
|
{
|
|
if (model.CreatedAt is not null)
|
|
{
|
|
try
|
|
{
|
|
var utcDate = model.CreatedAt.ToDateTime();
|
|
return utcDate.ToLocalTime().MiladiToJalaliWithTime();
|
|
}
|
|
catch
|
|
{
|
|
// ignore conversion issues and fall through to default
|
|
}
|
|
}
|
|
|
|
return DateTime.Now.MiladiToJalaliWithTime();
|
|
}
|
|
|
|
private static string ResolveDescription(CustomerWalletHistoryModel model)
|
|
{
|
|
var parts = new List<string>();
|
|
|
|
if (model.ChangeValue != 0)
|
|
parts.Add(model.ChangeValue > 0 ? "شارژ اصلی" : "برداشت اصلی");
|
|
|
|
if (model.ChangeNerworkValue != 0)
|
|
parts.Add(model.ChangeNerworkValue > 0 ? "دریافت پاداش های دریافتی" : "برداشت پاداش های دریافتی");
|
|
|
|
if (model.ChangeDiscountValue != 0)
|
|
parts.Add(model.ChangeDiscountValue > 0 ? "شارژ اعتباری" : "خرید اعتباری");
|
|
|
|
return parts.Count > 0 ? string.Join(" | ", parts) : "تراکنش کیف پول";
|
|
}
|
|
|
|
public async Task<bool> RequestWithdrawalAsync(long payoutId, WithdrawalMethodClient method, string? iban)
|
|
{
|
|
var request = new CustomerWithdrawBalanceRequest
|
|
{
|
|
PayoutId = payoutId,
|
|
WithdrawalMethod = (int)method
|
|
};
|
|
if (!string.IsNullOrWhiteSpace(iban))
|
|
{
|
|
request.IbanNumber = iban;
|
|
}
|
|
try
|
|
{
|
|
await _client.CustomerWithdrawBalanceAsync(request);
|
|
return true;
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
throw new InvalidOperationException(ex.Status.Detail ?? "خطا در ثبت برداشت", ex);
|
|
}
|
|
}
|
|
|
|
public async Task<List<WalletWithdrawal>> GetWithdrawalsAsync(int? status = null)
|
|
{
|
|
try
|
|
{
|
|
var request = new GetCustomerWithdrawalsRequest();
|
|
if (status.HasValue)
|
|
{
|
|
request.Status = status.Value;
|
|
}
|
|
|
|
var response = await _client.GetCustomerWithdrawalsAsync(request);
|
|
return response.Models
|
|
.Select(m => new WalletWithdrawal(
|
|
m.Id,
|
|
m.WeekDefinitionId,
|
|
m.WeekDisplayName,
|
|
m.TotalAmount,
|
|
m.Status,
|
|
m.WithdrawalMethod,
|
|
m.IbanNumber,
|
|
m.Created?.ToDateTime().MiladiToJalaliWithTime() ?? "-"))
|
|
.ToList();
|
|
}
|
|
catch
|
|
{
|
|
return new List<WalletWithdrawal>();
|
|
}
|
|
}
|
|
|
|
public async Task<WithdrawalSettings> GetWithdrawalSettingsAsync()
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.GetCustomerWithdrawalSettingsAsync(new Empty());
|
|
return new WithdrawalSettings(response.MinWithdrawalAmount);
|
|
}
|
|
catch
|
|
{
|
|
// Return default settings if backend is unavailable
|
|
return new WithdrawalSettings(0);
|
|
}
|
|
}
|
|
|
|
// ============= Magic Wallet =============
|
|
|
|
public async Task<MagicWalletStatus> GetMagicWalletStatusAsync()
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.GetMagicWalletStatusAsync(new Empty());
|
|
DateTime? activatedAt = response.MagicActivatedAt != null
|
|
? response.MagicActivatedAt.ToDateTime().ToLocalTime()
|
|
: null;
|
|
|
|
return new MagicWalletStatus(
|
|
response.WalletMode,
|
|
response.MagicTotalDeposited,
|
|
response.MagicTotalCredited,
|
|
response.MagicMaxDeposit,
|
|
response.MagicRemainingDeposit,
|
|
response.Balance,
|
|
activatedAt,
|
|
response.PurchaseCycleCount,
|
|
response.MagicMultiplier > 0 ? response.MagicMultiplier : 2.5,
|
|
response.MagicMaxCredit > 0 ? response.MagicMaxCredit : 2_500_000_000
|
|
);
|
|
}
|
|
catch
|
|
{
|
|
return new MagicWalletStatus(0, 0, 0, 0, 0, 0, null, 0);
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Success, string? GatewayUrl, string? Error)> InitiateMagicChargeAsync(long amount)
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.InitiateMagicChargeAsync(new InitiateMagicChargeRequest
|
|
{
|
|
Amount = amount
|
|
});
|
|
|
|
if (response.IsSuccess)
|
|
return (true, response.GatewayUrl, null);
|
|
|
|
return (false, null, response.ErrorMessage);
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
return (false, null, ex.Status.Detail ?? "خطا در ارتباط با سرور");
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Success, string? GatewayUrl, string? Error)> InitiateDiscountChargeAsync(long amount)
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.InitiateDiscountChargeAsync(new InitiateDiscountChargeRequest
|
|
{
|
|
Amount = amount
|
|
});
|
|
|
|
if (response.IsSuccess)
|
|
return (true, response.GatewayUrl, null);
|
|
|
|
return (false, null, response.ErrorMessage);
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
return (false, null, ex.Status.Detail ?? "خطا در ارتباط با سرور");
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Success, string? GatewayUrl, string? Error)> InitiateCreditChargeAsync(long amount)
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.InitiateCreditChargeAsync(new InitiateCreditChargeRequest
|
|
{
|
|
Amount = amount
|
|
});
|
|
|
|
if (response.IsSuccess)
|
|
return (true, response.GatewayUrl, null);
|
|
|
|
return (false, null, response.ErrorMessage);
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
return (false, null, ex.Status.Detail ?? "خطا در ارتباط با سرور");
|
|
}
|
|
}
|
|
|
|
// ============= Wallet Verify Methods =============
|
|
|
|
public async Task<(bool Success, string Message)> VerifyMagicChargeAsync(string authority, string status)
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.VerifyMagicChargeAsync(new VerifyWalletChargeRequest
|
|
{
|
|
Authority = authority,
|
|
Status = status
|
|
});
|
|
|
|
return (response.Success, response.Message);
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
return (false, ex.Status.Detail ?? "خطا در بررسی وضعیت پرداخت");
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Success, string Message)> VerifyDiscountChargeAsync(string authority, string status)
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.VerifyDiscountChargeAsync(new VerifyWalletChargeRequest
|
|
{
|
|
Authority = authority,
|
|
Status = status
|
|
});
|
|
|
|
return (response.Success, response.Message);
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
return (false, ex.Status.Detail ?? "خطا در بررسی وضعیت پرداخت");
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Success, string Message)> VerifyCreditChargeAsync(string authority, string status)
|
|
{
|
|
try
|
|
{
|
|
var response = await _client.VerifyCreditChargeAsync(new VerifyWalletChargeRequest
|
|
{
|
|
Authority = authority,
|
|
Status = status
|
|
});
|
|
|
|
return (response.Success, response.Message);
|
|
}
|
|
catch (Grpc.Core.RpcException ex)
|
|
{
|
|
return (false, ex.Status.Detail ?? "خطا در بررسی وضعیت پرداخت");
|
|
}
|
|
}
|
|
|
|
public int GetWalletMode()
|
|
{
|
|
try
|
|
{
|
|
var response = _client.GetCustomerWallet(new Empty());
|
|
return response.WalletMode;
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|