Skip to content

Latest commit

 

History

History
90 lines (72 loc) · 2.8 KB

File metadata and controls

90 lines (72 loc) · 2.8 KB

Data Model: P2P Payment Request

Prisma Schema

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

enum RequestStatus {
  PENDING
  PAID
  DECLINED
  EXPIRED
  CANCELED
}

// User representation based on Supabase Auth
model User {
  id               String           @id @default(uuid()) // Maps to auth.users.id
  email            String           @unique
  name             String
  phone            String?          // E.164 formatted
  
  // Relationships
  sentRequests     PaymentRequest[] @relation("SentRequests")
  receivedRequests PaymentRequest[] @relation("ReceivedRequests")
  
  createdAt        DateTime         @default(now())
  updatedAt        DateTime         @updatedAt
}

model PaymentRequest {
  id               String        @id @default(uuid())
  
  // Relationships
  requesterId      String
  requester        User          @relation("SentRequests", fields: [requesterId], references: [id])
  
  recipientId      String?       // Populated if recipientContact matches a user email
  recipient        User?         @relation("ReceivedRequests", fields: [recipientId], references: [id])
  
  // Core attributes
  recipientContact String        // Email or E.164 phone string
  amountCents      Int           // Positive integer
  currency         String        @default("USD")
  note             String?       @db.VarChar(280)
  status           RequestStatus @default(PENDING)
  shareToken       String        @unique @default(uuid())
  
  // Timestamps
  createdAt        DateTime      @default(now())
  expiresAt        DateTime      // Required: createdAt + 7 days
  paidAt           DateTime?
  declinedAt       DateTime?
  canceledAt       DateTime?
  updatedAt        DateTime      @updatedAt

  @@index([recipientContact])
  @@index([requesterId])
  @@index([recipientId])
  @@index([shareToken])
}

Validation & State Rules

  1. Amount Format & Storage:

    • Input validation enforces > $0.00 and <= 2 decimal places.
    • Values are multiplied by 100 and stored as amountCents integer.
    • UI converts back to $XX.YY for display.
  2. Recipient Contact Validation:

    • Must be a valid email or strictly E.164 phone number.
    • Saved exactly as entered or normalized to lowercase if email.
    • Contact matching queries (to link recipientId) are case-insensitive on email.
  3. Status Transitions (Strict Mode):

    • Modifications requiring state change must lock the record if possible or use transaction.
    • Expired rule is passive (expiresAt < now()), but state actions must check it to explicitly block PAID / DECLINED / CANCELED on expired claims.
  4. Security Bounds:

    • requesterId immutable after creation.
    • amountCents immutable after creation.
    • currency fixed to USD.