# Refund initiated within configured timeframe of cancellation

- **Pattern:** `ab-001144` (`ecommerce-order-management.cancellation-refunds.refund-timeframe`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001144
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001144)

## Why it matters

Without a defined refund window, refunds can theoretically be issued for orders cancelled two years ago — a refund policy violation that a simple date comparison prevents. Beyond fraud prevention, the absence of a configured timeframe means your refund policy (stated in terms and conditions) and your code are inconsistent: the policy says '30 days', the system enforces no limit. CWE-285 applies when a time-based access control exists in policy but not in code. The fix is a single numeric constant — `REFUND_WINDOW_DAYS` in `lib/orders/config.ts` — that makes the policy machine-enforceable and auditable.

## Severity rationale

Low because the practical risk requires a customer to request a refund long after the window and the system to process it, which is unlikely but not impossible without the guard.

## Remediation

Define the refund window as a named constant in `lib/orders/config.ts` and enforce it with a date comparison at the start of the refund handler at `app/api/orders/[id]/refund/route.ts`.

```ts
// lib/orders/config.ts
export const REFUND_WINDOW_DAYS = 30

// app/api/orders/[id]/refund/route.ts
import { REFUND_WINDOW_DAYS } from '@/lib/orders/config'

const daysSinceCancellation = Math.floor(
  (Date.now() - order.cancelledAt!.getTime()) / (1000 * 60 * 60 * 24)
)
if (daysSinceCancellation > REFUND_WINDOW_DAYS) {
  return Response.json(
    { error: `Refund window of ${REFUND_WINDOW_DAYS} days has passed.` },
    { status: 409 }
  )
}
```

## Detection

- **ID:** `refund-timeframe`
- **Severity:** `low`
- **What to look for:** Check for refund processing logic that enforces or references a defined timeframe for refund eligibility (e.g., "refunds must be initiated within 30 days of cancellation"). Look for at least 1 date comparison in refund handlers — something like `if (daysSinceCancellation > 30) throw new Error(...)`. Also check for a configuration constant that defines the refund window duration (at least 1 numeric constant). Quote the exact timeframe value if one exists. Count the number of refund handlers that enforce this window.
- **Pass criteria:** The refund logic includes or references a defined timeframe (at least 1 numeric constant, e.g., `REFUND_WINDOW_DAYS = 30`) for refund initiation after cancellation. The refund handler validates the order's cancellation date against this window and rejects out-of-window refund requests with an error message. At least 1 date comparison exists in the refund path.
- **Fail criteria:** No timeframe concept exists in the codebase (0 date comparisons in refund handlers) — refunds can theoretically be initiated for any order regardless of when it was cancelled. No date validation logic exists.
- **Skip (N/A) when:** The project does not support refunds. Or the project's refund policy is "all cancellations are immediately refunded" and refunds are processed as part of the cancellation flow, making a separate timeframe concept unnecessary. No separate refund endpoint exists.
- **Detail on fail:** `"No refund timeframe validation found. The refund handler at src/app/api/orders/[id]/refund/route.ts contains 0 date comparisons. Refunds could theoretically be issued for orders cancelled years ago."`
- **Remediation:** Add timeframe configuration in `lib/orders/config.ts` and validation in the refund handler:

  ```ts
  // lib/orders/config.ts
  export const REFUND_WINDOW_DAYS = 30

  // In refund handler (app/api/orders/[id]/refund/route.ts):
  import { REFUND_WINDOW_DAYS } from '@/lib/orders/config'

  const cancelledAt = order.cancelledAt
  if (!cancelledAt) {
    return Response.json({ error: 'Order is not cancelled' }, { status: 409 })
  }

  const daysSinceCancellation = Math.floor(
    (Date.now() - cancelledAt.getTime()) / (1000 * 60 * 60 * 24)
  )

  if (daysSinceCancellation > REFUND_WINDOW_DAYS) {
    return Response.json(
      { error: `Refund window of ${REFUND_WINDOW_DAYS} days has passed.` },
      { status: 409 }
    )
  }
  ```

---

## External references

- cwe CWE-285
- iso-25010 functional-correctness

Taxons: data-integrity, access-control

HTML version: https://auditbuffet.com/patterns/ab-001144
