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
13 changes: 7 additions & 6 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,19 +148,20 @@ POST /api/invoices/:id/publish # Publish to marketplace
POST /api/invoices/:id/payment # Record payment
```

### Marketplace
```
GET /api/v1/marketplace/invoices # Browse published invoices (public, no auth required)
```

**Note**: The marketplace endpoint provides public read access to published invoices for investor discovery. See [docs/MARKETPLACE_API.md](docs/MARKETPLACE_API.md) for detailed documentation.

### Investments
```
GET /api/investments # List user investments
POST /api/investments # Invest in invoice
GET /api/investments/:id # Get investment details
```

### Marketplace
```
GET /api/marketplace # Browse available invoices
GET /api/marketplace/stats # Market statistics
```

### Dashboard
```
GET /api/dashboard/seller # Seller analytics
Expand Down
192 changes: 192 additions & 0 deletions docs/MARKETPLACE_API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Marketplace API Documentation

The Marketplace API provides a read-only interface for investors to discover published receivables (invoices) available for investment. This decentralized marketplace allows investors to browse investment opportunities without exposing unnecessary seller PII.

## Overview

The marketplace endpoint allows unauthenticated access to published invoices, providing essential investment signals like discount rates, amounts, and due dates while protecting sensitive seller information.

## API Endpoint

### List Published Invoices

**GET** `/api/v1/marketplace/invoices`

Returns a paginated list of invoices available for investment.

#### Authentication
- **No authentication required** - Public read access for published invoices only
- This reduces friction for investors to discover opportunities

#### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number (min: 1) |
| `limit` | integer | 20 | Items per page (min: 1, max: 100) |
| `status` | string/array | `["published"]` | Invoice status filter |
| `dueBefore` | ISO date | - | Filter invoices due before this date |
| `minAmount` | number | - | Minimum invoice amount |
| `maxAmount` | number | - | Maximum invoice amount |
| `sort` | string | `"due_date"` | Sort field: `due_date`, `discount_rate`, `amount`, `created_at` |
| `sortOrder` | string | `"ASC"` | Sort order: `ASC` or `DESC` |

#### Response Format

**Success (200)**
```json
{
"success": true,
"data": [
{
"id": "uuid",
"invoiceNumber": "INV-001",
"customerName": "Customer Corp",
"amount": "10000.00",
"discountRate": "5.50",
"netAmount": "9450.00",
"dueDate": "2024-12-31T00:00:00.000Z",
"status": "published",
"createdAt": "2024-01-15T10:30:00.000Z"
}
],
"meta": {
"total": 150,
"page": 1,
"limit": 20,
"totalPages": 8
}
}
```

**Error Responses**
- `400` - Invalid query parameters
- `500` - Server error

## Public Fields

The API exposes only investor-relevant fields while protecting seller privacy:

### ✅ **Public Fields (Exposed)**
- `id` - Invoice identifier for investment references
- `invoiceNumber` - Public invoice reference
- `customerName` - Debtor information (relevant for credit assessment)
- `amount` - Full invoice value
- `discountRate` - Discount percentage offered
- `netAmount` - Amount after discount (what seller receives)
- `dueDate` - Payment due date (tenor information)
- `status` - Current invoice status
- `createdAt` - When invoice was created

### ❌ **Private Fields (Hidden)**
- `sellerId` - Seller identity protection
- `ipfsHash` - Internal document references
- `riskScore` - Internal risk assessments
- `smartContractId` - Technical implementation details
- `updatedAt` - Internal timestamps
- `deletedAt` - Soft deletion markers

## Privacy Rationale

The field selection balances investor needs with seller privacy:

1. **Investment Signals**: Discount rate, amount, and due date provide essential yield and risk information
2. **Credit Assessment**: Customer name allows investors to evaluate debtor creditworthiness
3. **Privacy Protection**: Seller identity and internal risk scores remain confidential
4. **Operational Security**: Technical details like IPFS hashes and contract IDs are hidden

## Usage Examples

### Basic Listing
```bash
curl "https://api.stellarsettle.com/api/v1/marketplace/invoices"
```

### Filtered Search
```bash
curl "https://api.stellarsettle.com/api/v1/marketplace/invoices?minAmount=1000&maxAmount=50000&sort=discount_rate&sortOrder=DESC"
```

### Date-Based Filtering
```bash
curl "https://api.stellarsettle.com/api/v1/marketplace/invoices?dueBefore=2024-12-31T23:59:59.999Z&sort=due_date"
```

### Pagination
```bash
curl "https://api.stellarsettle.com/api/v1/marketplace/invoices?page=2&limit=50"
```

### Multiple Status Filter
```bash
curl "https://api.stellarsettle.com/api/v1/marketplace/invoices?status=published&status=funded"
```

## Filtering & Sorting

### Status Filtering
- **Default**: Only `published` invoices (ready for investment)
- **Available statuses**: `published`, `funded`, `settled` (though `funded` and `settled` are less relevant for new investments)
- **Multiple values**: Use array format or repeat parameter

### Amount Filtering
- Both `minAmount` and `maxAmount` are optional
- Validation ensures `minAmount ≤ maxAmount`
- Useful for portfolio size constraints

### Date Filtering
- `dueBefore`: Find invoices with shorter tenors
- ISO 8601 date format required
- Timezone-aware filtering

### Sorting Options
- **`due_date`**: Sort by payment due date (tenor)
- **`discount_rate`**: Sort by yield/discount offered
- **`amount`**: Sort by invoice size
- **`created_at`**: Sort by listing recency

## Pagination

- **Stable ordering**: Results use `ORDER BY {sort_field} {order}, id ASC` for consistent pagination
- **Reasonable limits**: Maximum 100 items per page to prevent abuse
- **Complete metadata**: Response includes total count and page calculations

## Performance Considerations

- **Database indexes**: Optimized queries on `status`, `due_date`, `amount`, and `created_at`
- **Soft deletes**: Automatically excludes deleted invoices
- **Efficient counting**: Separate count query for accurate totals

## Integration Notes

### Frontend Integration
```javascript
// Fetch marketplace data
const response = await fetch('/api/v1/marketplace/invoices?page=1&limit=20&sort=discount_rate&sortOrder=DESC');
const { data, meta } = await response.json();

// Display investment opportunities
data.forEach(invoice => {
console.log(`${invoice.invoiceNumber}: ${invoice.discountRate}% discount, due ${invoice.dueDate}`);
});
```

### Investment Flow
1. **Discovery**: Browse marketplace for suitable invoices
2. **Analysis**: Evaluate discount rate, amount, tenor, and debtor
3. **Investment**: Use separate investment API (out of scope for this endpoint)

## Security & Privacy

- **No authentication required**: Reduces friction for discovery
- **Public data only**: No sensitive seller information exposed
- **Rate limiting**: Standard API rate limits apply
- **CORS enabled**: Supports browser-based applications

## Future Enhancements

Potential future features (out of current scope):
- Full-text search across invoice documents
- Advanced filtering (industry, geography, credit ratings)
- Real-time updates via WebSocket
- Bulk export capabilities
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import { createRequestObservabilityMiddleware } from "./middleware/request-obser
import { logger, type AppLogger } from "./observability/logger";
import { getMetricsContentType, MetricsRegistry } from "./observability/metrics";
import { createAuthRouter } from "./routes/auth.routes";
import { createMarketplaceRouter } from "./routes/marketplace.routes";
import type { AuthService } from "./services/auth.service";
import type { MarketplaceService } from "./services/marketplace.service";

export interface AppDependencies {
authService: AuthService;
marketplaceService?: MarketplaceService;
logger?: AppLogger;
metricsEnabled?: boolean;
metricsRegistry?: MetricsRegistry;
Expand Down Expand Up @@ -105,6 +108,7 @@ export function createRequestLifecycleTracker(): RequestLifecycleTracker {

export function createApp({
authService,
marketplaceService,
logger: appLogger = logger,
metricsEnabled = true,
metricsRegistry = new MetricsRegistry(),
Expand Down Expand Up @@ -167,6 +171,13 @@ export function createApp({

app.use("/api/v1/auth", createAuthRouter(authService));

// Add marketplace routes if service is provided
if (marketplaceService) {
app.use("/api/v1/marketplace", createMarketplaceRouter({
marketplaceService,
}));
}

app.use(notFoundMiddleware);
app.use(createErrorMiddleware(appLogger));
app.locals.requestLifecycleTracker = requestLifecycleTracker;
Expand Down
Loading
Loading