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)
{