feat: add PaymentTransaction table for gateway-level tracking
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:
masoodafar-web
2026-02-15 23:53:28 +03:30
parent 207f53ef80
commit a39a36e66d
15 changed files with 5017 additions and 5 deletions
@@ -0,0 +1,51 @@
using CMSMicroservice.Domain.Entities.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
public class PaymentTransactionConfiguration : IEntityTypeConfiguration<PaymentTransaction>
{
public void Configure(EntityTypeBuilder<PaymentTransaction> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
// اطلاعات درخواست
builder.Property(e => e.GatewayProvider).IsRequired().HasMaxLength(50);
builder.Property(e => e.MerchantId).IsRequired().HasMaxLength(200);
builder.Property(e => e.Amount).IsRequired();
builder.Property(e => e.CallbackUrl).IsRequired().HasMaxLength(500);
builder.Property(e => e.Description).IsRequired().HasMaxLength(500);
builder.Property(e => e.Mobile).HasMaxLength(20);
builder.Property(e => e.UserId);
// پاسخ درخواست
builder.Property(e => e.RequestStatusCode);
builder.Property(e => e.RequestStatusMessage).HasMaxLength(500);
builder.Property(e => e.Authority).HasMaxLength(200);
// وضعیت پرداخت
builder.Property(e => e.PaymentStatus).IsRequired();
// نتیجه verify
builder.Property(e => e.VerificationStatusCode);
builder.Property(e => e.VerificationStatusMessage).HasMaxLength(500);
builder.Property(e => e.CardHash).HasMaxLength(200);
builder.Property(e => e.CardPan).HasMaxLength(30);
builder.Property(e => e.RefId).HasMaxLength(200);
// ارتباط داخلی
builder.Property(e => e.TransactionId);
builder.Property(e => e.OrderId).HasMaxLength(100);
// ایندکس‌ها
builder.HasIndex(e => e.Authority);
builder.HasIndex(e => e.GatewayProvider);
builder.HasIndex(e => e.UserId);
builder.HasIndex(e => e.TransactionId);
builder.HasIndex(e => e.RefId);
}
}