Builds secondary market listing and purchase endpoints for fractional invoice shares, allowing investors to list their fractional holdings for resale and other investors to purchase from those listings.
POST /secondary-market/listings: Creates a resale listing with price and quantity.- Validates that the seller owns sufficient confirmed shares in the invoice, accounting for existing active listings to prevent double-listing.
- Validates invoice status (only funded invoices can be listed).
- Rejects listing shares on invoices that are past maturity with HTTP 422 (
INVOICE_MATURED).
GET /secondary-market/listings: Returns active listings per invoice with support for filtering (invoiceId,sellerWallet,minPrice,maxPrice) and pagination (page,limit).POST /secondary-market/listings/:id/buy: Executes purchases of fractional invoice shares from active listings.- Rejects over-purchases exceeding available listing quantity with HTTP 409 (
OVER_PURCHASE). - Rejects self-purchases and purchases on expired or non-active listings with HTTP 400.
- Atomically transfers ownership with payment in a single database transaction:
- Deducts purchased quantity from seller's confirmed holdings (
Investment). - Credits purchased quantity to buyer's confirmed holdings (
Investment). - Updates listing quantity/status (
SOLDif fully bought). - Records completed payment transactions (
Transaction).
- Deducts purchased quantity from seller's confirmed holdings (
- Rejects over-purchases exceeding available listing quantity with HTTP 409 (
- Cancel Listing Endpoints:
DELETE /secondary-market/listings/:idandPOST /secondary-market/listings/:id/cancel- Restricts cancellation exclusively to the listing owner (
sellerWalletorsellerId), returning HTTP 403 (FORBIDDEN) for non-owners.
- Route Mounting:
- Mounted on
/secondary-market,/api/v1/secondary-market, and/api/v1/secondaryfor complete URL compatibility.
- Mounted on
Closes #474
Implements KYC document submission, verification status checks, admin review transitions, and invoice submission gating as requested in Issue #475.
POST /kyc/submit:- Accepts seller details (
legalName,registrationNumber,jurisdiction, etc.) and IPFS document CIDs (documentCids,documentType,documentNumber,ipfsDocumentUrl). - Validates payload structure and stores document CIDs within verification records.
- Sets KYC status to
PENDING. - Rejects new submissions with HTTP 409 Conflict if a verification is already
PENDINGorAPPROVED.
- Accepts seller details (
GET /kyc/status:- Returns current verification status (
PENDING,APPROVED,REJECTED) for the authenticated seller/wallet. - Returns
rejectionReasonwhen status isREJECTED, andnullotherwise.
- Returns current verification status (
- Admin
PATCH /kyc/:id/review:- Allows administrators (authenticated via
x-admin-keyheader or admin JWT) to update verification status toAPPROVEDorREJECTED. - Requires rejection reason when rejecting verification.
- Dispatches notifications (
KYC_APPROVED/KYC_REJECTED) and logs audit trail vialogKYCStatusChange. - Also mounted on
/admin/kyc/:id/reviewand/api/v1/admin/kyc/:id/review.
- Allows administrators (authenticated via
- Re-submission on Rejected Status:
- Permitted when user's KYC status is
REJECTED. - Archives previous verification record in
KycHistoryRepository. - Resets status to
PENDING, updates document CIDs, and clears previous rejection reason.
- Permitted when user's KYC status is
- Invoice Submission KYC Gating:
- Invoice submission (
POST /invoicesandPOST /api/v1/invoices) checks seller KYC verification. - Blocks wallets without approved KYC (
user.kycStatus === 'approved'oruser.isKycVerified === true) with HTTP 403 (KYC_APPROVAL_REQUIRED), unlessskipVerificationis enabled in configuration.
- Invoice submission (
- Backwards Compatibility:
- All endpoints are accessible both directly on
/kycand under the/api/v1/kycprefix. - Legacy webhook and submission routes remain functional.
- All endpoints are accessible both directly on
Closes #475
- π Bug fix (non-breaking change which fixes an issue)
- β¨ New feature (non-breaking change which adds functionality)
- π₯ Breaking change (fix or feature that would cause existing functionality to not work as expected)
- π Documentation update
- π¨ UI/UX improvement
- β»οΈ Code refactoring
- β Test addition or update
- π§ Configuration change
- All GitHub Actions workflows are green on this PR (required for merge)
- Commit messages follow Conventional Commits (
feat:,fix:,chore:, etc.) β enforced by CI - No secrets, API keys,
.env, or credentials committed (seeCONTRIBUTING.md) - My code follows the code style of this project
- I have performed a self-review of my own code
- I have commented my code, particularly in hard-to-understand areas
- I have made corresponding changes to the documentation
- My changes generate no new warnings
- I have added tests that prove my fix is effective or that my feature works
- New and existing unit tests pass locally with my changes
- Any dependent changes have been merged and published
- Run secondary market test suite:
npx jest tests/unit/secondary-market.test.ts tests/secondary-market-notifications.test.ts
- Run KYC workflow test suite:
npx jest tests/unit/kyc-workflow.test.ts tests/kyc.service.test.ts tests/kyc.controller.test.ts
- Run TypeScript type check:
npm run type-check
- Run ESLint:
npm run lint
- Unit tests added/updated (
tests/unit/secondary-market.test.ts,tests/unit/kyc-workflow.test.ts) - Integration tests added/updated
- E2E tests added/updated (if applicable)
- Manual testing completed
- Code quality and readability
- Test coverage
- Security implications
- Performance impact
- Breaking changes