Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<!-- Title: use conventional commit style: feat|fix|docs|chore(scope): short description -->

## Summary

Provide a concise description of the change and the motivation.

## Related Issue

Closes #129

## What I changed

- Added Escrow, Dispute and AuditLog Mongoose models
- Implemented a `DisputeService` with lock and resolution logic
- Added `DisputeController` and versioned routes under `/api/v1/disputes`
- Added audit logging for admin actions

## Files of interest

- src/models/Escrow.ts
- src/models/Dispute.ts
- src/models/AuditLog.ts
- src/services/disputeService.ts
- src/controllers/disputeController.ts
- src/routes/disputeRoutes.ts
- src/routes/index.ts

## How to test locally

1. Ensure `.env` contains a working `MONGODB_URI` (or run MongoDB locally).
2. Install dependencies:

```bash
pnpm install
```

3. Run tests:

```bash
pnpm test
```

4. Start the dev server:

```bash
pnpm run dev
```

5. Example API calls (adjust host/port if needed):

Lock an escrow (admin):

```bash
curl -X POST http://localhost:3000/api/v1/disputes/<ESCROW_ID>/lock \
-H "Content-Type: application/json" \
-d '{"adminId":"<ADMIN_USER_ID>", "reason":"Buyer reports damaged goods"}'
```

Resolve a dispute (refund or release):

```bash
curl -X POST http://localhost:3000/api/v1/disputes/<DISPUTE_ID>/resolve \
-H "Content-Type: application/json" \
-d '{"adminId":"<ADMIN_USER_ID>", "action":"refund", "notes":"Refund approved"}'
```

Fetch dispute details:

```bash
curl http://localhost:3000/api/v1/disputes/<DISPUTE_ID>
```

## Acceptance Criteria Mapping

- Controller -> Service -> Model architecture: implemented
- Persistence: data saved to MongoDB; no hardcoded mocks
- API Versioning: endpoints registered under `/api/v1`
- Robust error handling: service and controller validate inputs and return errors

## Checklist

- [ ] Code follows repo conventions and lints
- [ ] Unit/integration tests added for critical logic (could be added in follow-up)
- [x] All existing tests pass
- [x] PR references related issue: Closes #129

## Proof of Work

Attach a screenshot of successful API response or test output. Example: terminal output of `pnpm test` or a Postman request showing success.

## Notes for reviewers

- This PR adds new models and routes; ensure DB indices and migration steps are acceptable.
- Admin authorization is not enforced in this change — recommend adding RBAC middleware in a follow-up.
47 changes: 47 additions & 0 deletions PR_DOCS/129-escrow-dispute-resolution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# PR: Escrow Dispute Resolution (Closes #129)

Summary
-------

This PR adds an Escrow Dispute Resolution flow that allows admins to lock escrows, create dispute records, and resolve disputes by refunding the buyer or releasing funds to the driver. It implements a Controller -> Service -> Model layered architecture and records every admin action in an Audit Log.

Key changes
-----------

- `src/models/Escrow.ts` — Escrow schema and status fields
- `src/models/Dispute.ts` — Dispute schema with history entries
- `src/models/AuditLog.ts` — AuditLog schema to capture actions
- `src/services/disputeService.ts` — Business logic for locking and resolving escrows
- `src/controllers/disputeController.ts` — API handlers for disputes
- `src/routes/disputeRoutes.ts` — Routes registered under `/api/v1/disputes`
- `src/routes/index.ts` — Registered the disputes routes

Why this change
---------------

To provide a manual arbitration mechanism enabling admins to temporarily lock funds in escrow and resolve disputes off-chain, while keeping a complete audit trail in the database.

How to run & verify
-------------------

1. Install deps: `pnpm install`
2. Run tests: `pnpm test` (existing test suite passed locally)
3. Start server: `pnpm run dev`
4. Use the example `curl` commands in the PR template to lock and resolve a dispute. Replace `<ESCROW_ID>`, `<DISPUTE_ID>`, and `<ADMIN_USER_ID>` where required.

Security & Follow-ups
---------------------

- This PR does not add RBAC enforcement — please ensure admin-only access is applied by adding existing auth middleware to the dispute routes.
- Consider adding integration tests around the dispute lifecycle.

Proof of Work
-------------

Attach here a screenshot of `pnpm test` output or a Postman screenshot showing a successful API response. The test run used in CI locally returned: `PASS tests/health.test.ts`.

Reviewer Notes
--------------

- Review schema choices and field names for consistency with existing models.
- Confirm whether `sorobanTxId` or additional blockchain fields should be set when resolving disputes.
25 changes: 25 additions & 0 deletions src/models/AuditLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import mongoose, { Document, Schema } from 'mongoose';

export interface IAuditLog extends Document {
action: string;
actor?: mongoose.Types.ObjectId | null;
targetType?: string;
targetId?: mongoose.Types.ObjectId | null;
description?: string;
meta?: Record<string, any> | null;
createdAt: Date;
}

const AuditLogSchema: Schema = new Schema(
{
action: { type: String, required: true },
actor: { type: Schema.Types.ObjectId, ref: 'User', default: null },
targetType: { type: String, default: null },
targetId: { type: Schema.Types.ObjectId, default: null },
description: { type: String, default: null },
meta: { type: Schema.Types.Mixed, default: null },
},
{ timestamps: { createdAt: true, updatedAt: false } },
);

export default mongoose.model<IAuditLog>('AuditLog', AuditLogSchema);
Loading