feat: add PaymentTransaction table for gateway-level tracking
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 9m6s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 9m6s
- New PaymentTransaction entity (Domain/Entities/Payment/) with all gateway fields: GatewayProvider, MerchantId, Authority, CardPan, CardHash, RefId, VerificationStatusCode, etc. - New PaymentTransactionConfiguration with indexes on Authority, GatewayProvider, UserId, TransactionId, RefId - Added DbSet<PaymentTransaction> to IApplicationDbContext and ApplicationDbContext - Extended PaymentVerificationResult DTO with CardPan, CardHash, VerificationCode - Updated ZarinPalPaymentService.VerifyPayment to return CardPan/CardHash/VerificationCode - Updated all 5 payment consumers to create/update PaymentTransaction: * PlaceOrderCommandHandler — creates PaymentTransaction after InitiatePayment * PaymentCallbackController — updates PaymentTransaction after VerifyPayment * ChargeDiscountWalletCommandHandler — creates PaymentTransaction + fixed callback URL * VerifyDiscountWalletChargeCommandHandler — updates PaymentTransaction after verify * TransactionsService.CustomerPaymentRequest/Verification — create/update PaymentTransaction * PackageService.CustomerPurchasePackage/Verify — create/update PaymentTransaction - Transaction table untouched — PaymentTransaction is a separate table - Pattern inspired by PYMS: create row before gateway → update after verify - EF migration: AddPaymentTransactionTable
This commit is contained in:
@@ -34,6 +34,7 @@ public interface IApplicationDbContext
|
||||
DbSet<UserWallet> UserWallets { get; }
|
||||
DbSet<UserWalletChangeLog> UserWalletChangeLogs { get; }
|
||||
DbSet<ManualPayment> ManualPayments { get; }
|
||||
DbSet<PaymentTransaction> PaymentTransactions { get; }
|
||||
DbSet<PublicMessage> PublicMessages { get; }
|
||||
DbSet<ClubMembership> ClubMemberships { get; }
|
||||
DbSet<ClubMembershipHistory> ClubMembershipHistories { get; }
|
||||
|
||||
@@ -142,6 +142,21 @@ public class PaymentVerificationResult
|
||||
/// پیام
|
||||
/// </summary>
|
||||
public string? Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// شماره کارت ماسکشده (مثلاً 6037-****-****-1234)
|
||||
/// </summary>
|
||||
public string? CardPan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// هش کارت بانکی
|
||||
/// </summary>
|
||||
public string? CardHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// کد وضعیت verify از درگاه (100=موفق، 101=تکراری)
|
||||
/// </summary>
|
||||
public int? VerificationCode { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+19
@@ -207,6 +207,25 @@ public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, Place
|
||||
{
|
||||
// ذخیره Authority/RefId در تراکنش برای verify بعدی
|
||||
transaction.RefId = paymentResult.RefId;
|
||||
|
||||
// ثبت PaymentTransaction — جدول جدید با اطلاعات درگاه
|
||||
var paymentTx = new PaymentTransaction
|
||||
{
|
||||
GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal",
|
||||
MerchantId = _configuration["ZarinPal:MerchantId"] ?? "",
|
||||
Amount = finalGatewayAmount,
|
||||
CallbackUrl = callbackUrl,
|
||||
Description = $"فروشگاه تخفیفی - سفارش #{order.Id}",
|
||||
Mobile = null,
|
||||
UserId = request.UserId,
|
||||
RequestStatusCode = 100,
|
||||
RequestStatusMessage = "Success",
|
||||
Authority = paymentResult.RefId,
|
||||
PaymentStatus = false, // هنوز verify نشده
|
||||
TransactionId = transaction.Id,
|
||||
OrderId = order.Id.ToString()
|
||||
};
|
||||
_context.PaymentTransactions.Add(paymentTx);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
paymentUrl = paymentResult.GatewayUrl;
|
||||
|
||||
+30
-3
@@ -2,8 +2,10 @@ using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Entities.Payment;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet;
|
||||
@@ -13,15 +15,18 @@ public class ChargeDiscountWalletCommandHandler
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IPaymentGatewayService _paymentGateway;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ChargeDiscountWalletCommandHandler> _logger;
|
||||
|
||||
public ChargeDiscountWalletCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IPaymentGatewayService paymentGateway,
|
||||
IConfiguration configuration,
|
||||
ILogger<ChargeDiscountWalletCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_paymentGateway = paymentGateway;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -58,12 +63,15 @@ public class ChargeDiscountWalletCommandHandler
|
||||
}
|
||||
|
||||
// 3. ایجاد درخواست پرداخت
|
||||
var cmsBaseUrl = _configuration["CmsBaseUrl"] ?? "https://localhost:32846";
|
||||
var callbackUrl = $"{cmsBaseUrl}/api/wallet/verify-discount-charge";
|
||||
|
||||
var paymentRequest = new PaymentRequest
|
||||
{
|
||||
Amount = request.Amount,
|
||||
UserId = user.Id,
|
||||
Mobile = user.Mobile ?? "",
|
||||
CallbackUrl = $"https://yourdomain.com/api/wallet/verify-discount-charge",
|
||||
CallbackUrl = callbackUrl,
|
||||
Description = $"شارژ کیف پول تخفیفی - کاربر {user.Id}"
|
||||
};
|
||||
|
||||
@@ -80,10 +88,29 @@ public class ChargeDiscountWalletCommandHandler
|
||||
throw new Exception($"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}");
|
||||
}
|
||||
|
||||
// 4. ثبت PaymentTransaction
|
||||
var paymentTx = new PaymentTransaction
|
||||
{
|
||||
GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal",
|
||||
MerchantId = _configuration["ZarinPal:MerchantId"] ?? "",
|
||||
Amount = request.Amount,
|
||||
CallbackUrl = callbackUrl,
|
||||
Description = $"شارژ کیف پول تخفیفی - کاربر {user.Id}",
|
||||
Mobile = user.Mobile,
|
||||
UserId = user.Id,
|
||||
RequestStatusCode = 100,
|
||||
RequestStatusMessage = "Success",
|
||||
Authority = paymentResult.RefId,
|
||||
PaymentStatus = false
|
||||
};
|
||||
_context.PaymentTransactions.Add(paymentTx);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Discount wallet charge initiated. UserId: {UserId}, RefId: {RefId}",
|
||||
"Discount wallet charge initiated. UserId: {UserId}, RefId: {RefId}, PaymentTxId: {PaymentTxId}",
|
||||
user.Id,
|
||||
paymentResult.RefId
|
||||
paymentResult.RefId,
|
||||
paymentTx.Id
|
||||
);
|
||||
|
||||
return paymentResult;
|
||||
|
||||
+20
@@ -56,6 +56,19 @@ public class VerifyDiscountWalletChargeCommandHandler
|
||||
"OK" // وقتی این handler فراخوانی میشه یعنی کاربر از درگاه برگشته — Status باید OK باشه
|
||||
);
|
||||
|
||||
// آپدیت PaymentTransaction با نتیجه verify
|
||||
var paymentTx = await _context.PaymentTransactions
|
||||
.FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken);
|
||||
if (paymentTx != null)
|
||||
{
|
||||
paymentTx.PaymentStatus = verifyResult.IsSuccess;
|
||||
paymentTx.VerificationStatusCode = verifyResult.VerificationCode;
|
||||
paymentTx.VerificationStatusMessage = verifyResult.Message;
|
||||
paymentTx.CardPan = verifyResult.CardPan;
|
||||
paymentTx.CardHash = verifyResult.CardHash;
|
||||
paymentTx.RefId = verifyResult.TrackingCode;
|
||||
}
|
||||
|
||||
if (!verifyResult.IsSuccess)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
@@ -101,6 +114,13 @@ public class VerifyDiscountWalletChargeCommandHandler
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// لینک PaymentTransaction به Transaction داخلی
|
||||
if (paymentTx != null)
|
||||
{
|
||||
paymentTx.TransactionId = transaction.Id;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Discount wallet charged successfully. UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}",
|
||||
user.Id,
|
||||
|
||||
Reference in New Issue
Block a user