Skip to content

About

A full-stack order operations app for tracking orders, partial payments, refunds, balances, audit history, and CSV exports.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Orders & Settlements

Orders & Settlements is a small order-operations workspace for tracking customer orders, partial payments, refunds, and outstanding balances.

I built it around one principle: money should be predictable. Order totals are calculated on the server and stored as integer cents, payment rules are enforced in the API, and important changes are recorded in an audit trail.

What is included

  • Email/password sign up and login
  • User-scoped data access
  • Orders with multiple line items
  • Server-side subtotal and total calculation
  • Pending, partially paid, paid, and overdue statuses
  • Partial payment recording with overpayment protection
  • Order detail view with payment history
  • Refunds with refundable-balance protection
  • Audit history for order creation, updates, payments, refunds, and status changes
  • Dashboard filtering by status
  • CSV export with an optional due-date range

Tech stack

  • Next.js App Router and TypeScript
  • React
  • MongoDB with Mongoose (transactions require Atlas or a replica-set-enabled local MongoDB)
  • JWT session cookie authentication
  • Zod request validation
  • Tailwind CSS

Running locally

Prerequisites

  • Node.js 20 or newer
  • A MongoDB database. MongoDB Atlas is the simplest option; local MongoDB must run as a replica set because the app uses transactions.

Setup

npm install

Create .env.local in the project root:

MONGODB_URI=your-mongodb-connection-string
JWT_SECRET=your-long-random-session-secret

JWT_SECRET should be a long, random value used only by this application. Do not commit .env.local or share either secret.

If you use local MongoDB, start it with replica-set support and initialize the replica set before using payment or refund flows. A standalone MongoDB process does not support the transactions used by this app.

Start the development server:

npm run dev

Open http://localhost:3000, create an account, and create an order.

Useful checks:

npm run lint
npm run build

Product rules and decisions

Money and totals

The UI accepts currency values, but the API receives and stores amounts in integer cents. For example, $1,000.00 is stored as 100000.

For each order line:

line total = quantity × unit price

The order subtotal and total are the sum of all line totals. There are no order-level taxes or discounts in this assignment.

Zero-value lines are allowed, but an order must have a total greater than $0.00. Quantity must be at least 1 and unit price cannot be negative.

Statuses

Status is derived from the order total, net paid amount, and due date:

Status Rule
pending No net payment has been recorded and the order is not past due
partially_paid Net payment is greater than zero but below the order total, and the order is not past due
paid Net paid amount is equal to or greater than the order total
overdue The due date has passed and the order is not fully paid

Paid takes precedence over overdue. This means an overdue order becomes paid as soon as its remaining balance is settled.

Payments

Multiple payments are supported. The API rejects a payment that exceeds the current amount due and returns the remaining balance in the error response.

Orders become read-only after the first payment. This prevents changing the order total after money has already been allocated against it. The rule is enforced by the API as well as reflected in the UI.

Refunds

Refunds are stored as a separate entity rather than as negative payments. A refund cannot exceed the net amount paid:

net paid = gross payments − refunds
refundable balance = gross payments − refunds already recorded

A refund can reopen a fully paid order by reducing its net paid amount. Refunds also appear in the order history and audit log.

Audit history

Audit events are written transactionally with the underlying order, payment, or refund operation. The order detail page shows creation, updates, payments, refunds, and status transitions with timestamps.

CSV export

The dashboard provides an optional due-date range filter. The export includes:

  • Order ID and customer
  • Due date and derived status
  • Subtotal and total
  • Gross paid
  • Refunded amount
  • Net paid
  • Amount due

Leaving either date empty keeps that boundary open.

API overview

All order endpoints require the authenticated session cookie.

Method Endpoint Purpose
POST /api/auth/signup Create an account and start a session
POST /api/auth/login Log in and start a session
POST /api/auth/logout Clear the current session
GET /api/auth/me Return the current user
GET /api/orders List the current user's orders; accepts status filter
POST /api/orders Create an order with line items
GET /api/orders/:id Load order details, payments, refunds, and audit history
PATCH /api/orders/:id Update an unpaid order
DELETE /api/orders/:id Delete an unpaid order
GET /api/orders/:id/payments List payments for an order
POST /api/orders/:id/payments Record a payment
GET /api/orders/:id/refunds List refunds for an order
POST /api/orders/:id/refunds Record a refund
GET /api/orders/:id/audit-log List audit events
GET /api/orders/export Download CSV; accepts optional from and to dates in YYYY-MM-DD format

Errors use a consistent shape:

{
  "error": {
    "message": "Payment exceeds the remaining balance",
    "code": "OVERPAYMENT",
    "remainingAmountCents": 60000
  }
}

Validation errors include an issues array from Zod with field-level details.

Concurrency and data integrity

Order creation, payment recording, refund recording, and audit writes use MongoDB transactions. Payment and refund operations reload the order inside the transaction before calculating the remaining balance, so competing writes are subject to MongoDB's transaction conflict handling rather than relying on client-side values.

Payment and refund requests also carry an idempotency key. Retrying the same request with the same key returns the original mutation instead of creating a duplicate financial event.

For a larger production system, I would add stronger operational safeguards around idempotency keys, payment-provider reconciliation, retry handling, and an explicit atomic conditional update strategy for very high write contention.

Deployment

The app can be deployed to Vercel with MongoDB Atlas:

  1. Create a MongoDB Atlas cluster and allow the deployment to connect.
  2. Import the repository into Vercel.
  3. Add MONGODB_URI and JWT_SECRET as Vercel environment variables for the appropriate environments.
  4. Deploy and verify /api/health.
  5. Test sign up, order creation, payment, refund, and CSV download on the deployed URL.

Live URL: https://orders-settlements.vercel.app/

What I would improve before production

  • Expand the included unit tests into integration tests for payment allocation, refund limits, and authorization boundaries.
  • Add idempotency keys for payment and refund requests.
  • Add structured logging, monitoring, and alerting.
  • Add pagination and aggregation queries for large order histories and exports.
  • Add role-based access control and stronger account recovery flows.
  • Integrate external payment providers with webhook reconciliation.
  • Add rate limiting and more granular security auditing.

Assignment verification flow

  1. Create an order for 2 × $500 = $1,000.
  2. Record a $400 payment. The order should show partially_paid and $600 due.
  3. Record a $600 payment. The order should show paid and $0 due.
  4. Try to record another $1 payment. The API should reject it as an overpayment.
  5. Record a partial refund and confirm that net paid, amount due, status, refund history, and audit history update together.

About

A full-stack order operations app for tracking orders, partial payments, refunds, balances, audit history, and CSV exports.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages