diff --git a/src/CMSMicroservice.Protobuf/Protos/userwallet.proto b/src/CMSMicroservice.Protobuf/Protos/userwallet.proto index 00431a2..d44af1e 100644 --- a/src/CMSMicroservice.Protobuf/Protos/userwallet.proto +++ b/src/CMSMicroservice.Protobuf/Protos/userwallet.proto @@ -88,6 +88,15 @@ service UserWalletContract get: "/Customer/GetMagicWalletStatus" }; }; + + // ============= Discount Wallet Methods ============= + + rpc InitiateDiscountCharge(InitiateDiscountChargeRequest) returns (InitiateDiscountChargeResponse){ + option (google.api.http) = { + post: "/Customer/InitiateDiscountCharge" + body: "*" + }; + }; } message CreateNewUserWalletRequest { @@ -241,4 +250,18 @@ message GetMagicWalletStatusResponse int64 balance = 6; google.protobuf.Timestamp magic_activated_at = 7; int32 purchase_cycle_count = 8; // تعداد دورهای خرید پکیج (0 = هنوز هیچ دوری تکمیل نشده) +} + +// ============= Discount Wallet Messages ============= + +message InitiateDiscountChargeRequest +{ + int64 amount = 1; // مبلغ واریزی (ریال) +} + +message InitiateDiscountChargeResponse +{ + bool is_success = 1; + string gateway_url = 2; + string error_message = 3; } \ No newline at end of file diff --git a/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs b/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs index c9525ed..404ce91 100644 --- a/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs +++ b/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs @@ -1,5 +1,6 @@ using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Application.DiscountShopCQ.Commands.CompleteOrderPayment; +using CMSMicroservice.Application.WalletCQ.Commands.VerifyDiscountWalletCharge; using CMSMicroservice.Application.WalletCQ.Commands.VerifyMagicWalletCharge; using MediatR; using Microsoft.AspNetCore.Authorization; @@ -186,4 +187,63 @@ public class PaymentCallbackController : ControllerBase return Redirect($"{frontOfficeBaseUrl}/magic-wallet?payment=failed"); } } + + /// + /// Callback برای شارژ کیف‌پول تخفیفی — زرین‌پال بعد از پرداخت کاربر را اینجا برمی‌گرداند + /// + [HttpGet("/api/wallet/verify-discount-charge")] + public async Task DiscountChargeCallback( + [FromQuery(Name = "Authority")] string? authority, + [FromQuery(Name = "Status")] string? status, + CancellationToken cancellationToken) + { + var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; + + _logger.LogInformation( + "Discount charge callback received: Authority={Authority}, Status={Status}", + authority, status); + + try + { + if (string.IsNullOrEmpty(authority)) + { + _logger.LogError("Discount charge callback: Authority is missing"); + return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=error&reason=no-authority"); + } + + // پیدا کردن PaymentTransaction برای استخراج UserId و Amount + var paymentTx = await _context.PaymentTransactions + .FirstOrDefaultAsync(pt => pt.Authority == authority, cancellationToken); + + if (paymentTx == null || !paymentTx.UserId.HasValue) + { + _logger.LogError("Discount charge callback: PaymentTransaction not found for Authority={Authority}", authority); + return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=error&reason=tx-not-found"); + } + + if (!string.Equals(status, "OK", StringComparison.OrdinalIgnoreCase)) + { + _logger.LogWarning("Discount charge cancelled by user. Authority={Authority}", authority); + return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=cancelled"); + } + + var result = await _sender.Send(new VerifyDiscountWalletChargeCommand + { + UserId = paymentTx.UserId.Value, + Amount = paymentTx.Amount, + Authority = authority + }, cancellationToken); + + _logger.LogInformation( + "Discount charge completed successfully. Authority={Authority}, UserId={UserId}", + authority, paymentTx.UserId.Value); + + return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=success"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Discount charge callback error. Authority={Authority}", authority); + return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=failed"); + } + } } diff --git a/src/CMSMicroservice.WebApi/Services/UserWalletService.cs b/src/CMSMicroservice.WebApi/Services/UserWalletService.cs index adcbdee..e94ed79 100644 --- a/src/CMSMicroservice.WebApi/Services/UserWalletService.cs +++ b/src/CMSMicroservice.WebApi/Services/UserWalletService.cs @@ -4,6 +4,7 @@ using CMSMicroservice.Application.UserWalletCQ.Commands.CreateNewUserWallet; using CMSMicroservice.Application.UserWalletCQ.Commands.UpdateUserWallet; using CMSMicroservice.Application.UserWalletCQ.Commands.DeleteUserWallet; using CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet; +using CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet; using CMSMicroservice.Application.UserWalletCQ.Queries.GetUserWallet; using CMSMicroservice.Application.UserWalletCQ.Queries.GetAllUserWalletByFilter; using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletChangeLog; @@ -170,6 +171,27 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase }; } + // ============= Discount Wallet Methods ============= + + public override async Task InitiateDiscountCharge( + InitiateDiscountChargeRequest request, ServerCallContext context) + { + var userId = GetCurrentUserId(); + + var result = await _sender.Send(new ChargeDiscountWalletCommand + { + UserId = userId, + Amount = request.Amount + }, context.CancellationToken); + + return new InitiateDiscountChargeResponse + { + IsSuccess = result.IsSuccess, + GatewayUrl = result.GatewayUrl ?? "", + ErrorMessage = result.ErrorMessage ?? "" + }; + } + public override async Task GetMagicWalletStatus( Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context) {