From 7a3931f2d4d6a348f4db931b5499bec083139590 Mon Sep 17 00:00:00 2001 From: Abubeker Yasin Date: Sat, 16 May 2026 20:42:54 +0300 Subject: [PATCH] Initialize Telebirr payment integration --- .../migration.sql | 63 +++++++++++++++++++ apps/edr-passenger-api/prisma/schema.prisma | 60 ++++++++++++++---- 2 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 apps/edr-passenger-api/prisma/migrations/20260516170103_add_telebirr_async_fields/migration.sql diff --git a/apps/edr-passenger-api/prisma/migrations/20260516170103_add_telebirr_async_fields/migration.sql b/apps/edr-passenger-api/prisma/migrations/20260516170103_add_telebirr_async_fields/migration.sql new file mode 100644 index 000000000..d3cd87873 --- /dev/null +++ b/apps/edr-passenger-api/prisma/migrations/20260516170103_add_telebirr_async_fields/migration.sql @@ -0,0 +1,63 @@ +/* + Warnings: + + - A unique constraint covering the columns `[merchantOrderId]` on the table `PaymentIntent` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "PaymentIntent" ADD COLUMN "expiresAt" TIMESTAMP(3), +ADD COLUMN "failureCode" TEXT, +ADD COLUMN "failureMessage" TEXT, +ADD COLUMN "merchantOrderId" TEXT, +ADD COLUMN "paidAt" TIMESTAMP(3), +ADD COLUMN "providerOrderId" TEXT, +ADD COLUMN "providerTxnId" TEXT, +ADD COLUMN "rawInitiation" JSONB; + +-- CreateTable +CREATE TABLE "PaymentWebhookEvent" ( + "id" TEXT NOT NULL, + "provider" "PaymentMethodType" NOT NULL, + "externalEventId" TEXT NOT NULL, + "merchantOrderId" TEXT, + "providerTxnId" TEXT, + "signatureValid" BOOLEAN NOT NULL, + "status" TEXT NOT NULL, + "payload" JSONB NOT NULL, + "receivedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "processedAt" TIMESTAMP(3), + "processingError" TEXT, + + CONSTRAINT "PaymentWebhookEvent_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PaymentRefund" ( + "id" TEXT NOT NULL, + "paymentIntentId" TEXT NOT NULL, + "amountMinor" INTEGER NOT NULL, + "reason" TEXT, + "providerRefundId" TEXT, + "status" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "PaymentRefund_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "PaymentWebhookEvent_merchantOrderId_idx" ON "PaymentWebhookEvent"("merchantOrderId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PaymentWebhookEvent_provider_externalEventId_key" ON "PaymentWebhookEvent"("provider", "externalEventId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PaymentIntent_merchantOrderId_key" ON "PaymentIntent"("merchantOrderId"); + +-- CreateIndex +CREATE INDEX "PaymentIntent_providerOrderId_idx" ON "PaymentIntent"("providerOrderId"); + +-- CreateIndex +CREATE INDEX "PaymentIntent_providerTxnId_idx" ON "PaymentIntent"("providerTxnId"); + +-- AddForeignKey +ALTER TABLE "PaymentRefund" ADD CONSTRAINT "PaymentRefund_paymentIntentId_fkey" FOREIGN KEY ("paymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/apps/edr-passenger-api/prisma/schema.prisma b/apps/edr-passenger-api/prisma/schema.prisma index f4103c7a6..89ba286e8 100644 --- a/apps/edr-passenger-api/prisma/schema.prisma +++ b/apps/edr-passenger-api/prisma/schema.prisma @@ -334,17 +334,55 @@ model PaymentMethod { } model PaymentIntent { - id String @id @default(uuid()) - bookingId String @unique - amountMinor Int - currency String @default("ETB") - method PaymentMethodType - status PaymentIntentStatus @default(REQUIRES_ACTION) - providerRef String? - clientAction Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - booking Booking @relation(fields: [bookingId], references: [id]) + id String @id @default(uuid()) + bookingId String @unique + amountMinor Int + currency String @default("ETB") + method PaymentMethodType + status PaymentIntentStatus @default(REQUIRES_ACTION) + providerRef String? + clientAction Json? + merchantOrderId String? @unique + providerOrderId String? + providerTxnId String? + rawInitiation Json? + paidAt DateTime? + failureCode String? + failureMessage String? + expiresAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + booking Booking @relation(fields: [bookingId], references: [id]) + refunds PaymentRefund[] + @@index([providerOrderId]) + @@index([providerTxnId]) +} + +model PaymentWebhookEvent { + id String @id @default(uuid()) + provider PaymentMethodType + externalEventId String + merchantOrderId String? + providerTxnId String? + signatureValid Boolean + status String + payload Json + receivedAt DateTime @default(now()) + processedAt DateTime? + processingError String? + @@unique([provider, externalEventId]) + @@index([merchantOrderId]) +} + +model PaymentRefund { + id String @id @default(uuid()) + paymentIntentId String + amountMinor Int + reason String? + providerRefundId String? + status String + createdAt DateTime @default(now()) + paymentIntent PaymentIntent @relation(fields: [paymentIntentId], references: [id]) } model Ticket {