Skip to content

[Phase 1] Refund & Return Workflow Primitives #72

@syed-reza98

Description

@syed-reza98

Priority: P0 (Critical)

Phase: 1 - E-Commerce Core
Estimate: 3 days
Type: Story

Context

Introduce primitives for refunds and returns to ensure financial integrity, inventory reconciliation, and customer trust. Provides groundwork for future RMA flows and partial refunds.

Scope

  • Models: RefundRequest, RefundTransaction, ReturnAuthorization
  • State machines (simplified initial):
    • RefundRequest: PENDING → APPROVED → EXECUTED / REJECTED
    • ReturnAuthorization: ISSUED → RECEIVED → RESTOCKED / DISCARDED
  • Refund triggers payment provider API (stub initially) & idempotent recording
  • Inventory restock on successful return (link to InventoryReservation & original Order lines)
  • Validation: Refund amount ≤ original paid minus prior refunds

Acceptance Criteria

  • Create RefundRequest validates amount boundaries
  • Executing refund logs structured event & updates PaymentAttempt state (link PaymentAttempt [Phase 1] PaymentAttempt & PaymentTransaction State Machine #63)
  • ReturnAuthorization receiving items updates inventory (restock or discard path)
  • Partial refunds supported (multiple RefundRequest until total reached)
  • Metrics: refund.request.count, refund.executed.amount.total
  • Audit log entries for each state transition

Data Model (Draft)

model RefundRequest {
  id            String   @id @default(cuid())
  orderId       String
  paymentAttemptId String?
  amount        Int
  status        RefundStatus @default(PENDING)
  reason        String?
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
}

enum RefundStatus {
  PENDING
  APPROVED
  EXECUTED
  REJECTED
}

model ReturnAuthorization {
  id         String   @id @default(cuid())
  orderId    String
  status     ReturnStatus @default(ISSUED)
  notes      String?
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt
}

enum ReturnStatus {
  ISSUED
  RECEIVED
  RESTOCKED
  DISCARDED
}

model RefundTransaction {
  id               String   @id @default(cuid())
  refundRequestId  String
  externalRef      String?
  amount           Int
  createdAt        DateTime @default(now())
}

Dependencies

Metrics Targets

  • 100% accurate cumulative refund amount tracking

Testing Checklist

  • Partial refund sequence doesn't exceed original amount
  • ReturnAuthorization restocks inventory correctly
  • Duplicate refund execution prevented by idempotency key

Risk

Financial & customer trust impact (score: 17). Required before advanced commerce expansion.

References

  • docs/GITHUB_ISSUES_COMPARISON_ANALYSIS.md (refund/return gap)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions