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
@@ -2328,6 +2328,114 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("ManualPayments", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.PaymentTransaction", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<long>("Amount")
.HasColumnType("bigint");
b.Property<string>("Authority")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("CallbackUrl")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("CardHash")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("CardPan")
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("GatewayProvider")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("MerchantId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Mobile")
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");
b.Property<string>("OrderId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<bool>("PaymentStatus")
.HasColumnType("bit");
b.Property<string>("RefId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<int?>("RequestStatusCode")
.HasColumnType("int");
b.Property<string>("RequestStatusMessage")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<long?>("TransactionId")
.HasColumnType("bigint");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.Property<int?>("VerificationStatusCode")
.HasColumnType("int");
b.Property<string>("VerificationStatusMessage")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.HasKey("Id");
b.HasIndex("Authority");
b.HasIndex("GatewayProvider");
b.HasIndex("RefId");
b.HasIndex("TransactionId");
b.HasIndex("UserId");
b.ToTable("PaymentTransactions", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Product", b =>
{
b.Property<long>("Id")