Severity: HIGH — Payouts Module is Schema-Only, No Business Logic
Problem
The Payout Prisma model exists and is well-defined, but the payouts NestJS module has no service or controller implementation. The API endpoints POST /v1/payouts and POST /v1/payouts/bulk documented in the Product Docs are non-functional.
Implementation Required
1. Payouts Service
```typescript
@Injectable()
export class PayoutsService {
async createSingle(merchantId: string, dto: CreatePayoutDto): Promise
async createBulk(merchantId: string, dto: BulkPayoutDto): Promise
async findAll(merchantId: string, filters: PayoutFiltersDto): Promise<PaginatedResult>
async findOne(merchantId: string, payoutId: string): Promise
async cancel(merchantId: string, payoutId: string): Promise
async retry(merchantId: string, payoutId: string): Promise
async processPayout(payoutId: string): Promise // called by BullMQ worker
}
```
2. Payout execution via Stellar
For STELLAR and CRYPTO_WALLET destination types:
```typescript
async processPayout(payoutId: string) {
const payout = await this.prisma.payout.findUnique({ where: { id: payoutId } });
// Convert merchant balance → payout currency via path payment
const txHash = await this.stellar.pathPaymentSend({
source: process.env.RELAY_STELLAR_ADDRESS,
destination: payout.destination.stellarAddress,
sendAsset: 'USDC',
sendAmount: payout.amount,
destAsset: payout.currency,
});
await this.prisma.payout.update({
where: { id: payoutId },
data: { status: 'COMPLETED', stellarTxHash: txHash, completedAt: new Date() },
});
await this.webhooksService.fire(payout.merchantId, 'payout.completed', payout);
await this.eventsService.emitPayoutStatus(payout.merchantId, payoutId, 'COMPLETED');
}
```
3. BullMQ worker for async processing
```typescript
@processor('payouts')
export class PayoutsProcessor {
@process('process-payout')
async processPayout(job: Job<{ payoutId: string }>) {
await this.payoutsService.processPayout(job.data.payoutId);
}
}
```
4. Bulk CSV parsing
Use csv-parser or papaparse to handle bulk uploads.
Row-level validation: return errors per row without failing the entire batch.
Acceptance Criteria
Severity: HIGH — Payouts Module is Schema-Only, No Business Logic
Problem
The Payout Prisma model exists and is well-defined, but the payouts NestJS module has no service or controller implementation. The API endpoints
POST /v1/payoutsandPOST /v1/payouts/bulkdocumented in the Product Docs are non-functional.Implementation Required
1. Payouts Service
```typescript
@Injectable()
export class PayoutsService {
async createSingle(merchantId: string, dto: CreatePayoutDto): Promise
async createBulk(merchantId: string, dto: BulkPayoutDto): Promise
async findAll(merchantId: string, filters: PayoutFiltersDto): Promise<PaginatedResult>
async findOne(merchantId: string, payoutId: string): Promise
async cancel(merchantId: string, payoutId: string): Promise
async retry(merchantId: string, payoutId: string): Promise
async processPayout(payoutId: string): Promise // called by BullMQ worker
}
```
2. Payout execution via Stellar
For
STELLARandCRYPTO_WALLETdestination types:```typescript
async processPayout(payoutId: string) {
const payout = await this.prisma.payout.findUnique({ where: { id: payoutId } });
// Convert merchant balance → payout currency via path payment
const txHash = await this.stellar.pathPaymentSend({
source: process.env.RELAY_STELLAR_ADDRESS,
destination: payout.destination.stellarAddress,
sendAsset: 'USDC',
sendAmount: payout.amount,
destAsset: payout.currency,
});
await this.prisma.payout.update({
where: { id: payoutId },
data: { status: 'COMPLETED', stellarTxHash: txHash, completedAt: new Date() },
});
await this.webhooksService.fire(payout.merchantId, 'payout.completed', payout);
await this.eventsService.emitPayoutStatus(payout.merchantId, payoutId, 'COMPLETED');
}
```
3. BullMQ worker for async processing
```typescript
@processor('payouts')
export class PayoutsProcessor {
@process('process-payout')
async processPayout(job: Job<{ payoutId: string }>) {
await this.payoutsService.processPayout(job.data.payoutId);
}
}
```
4. Bulk CSV parsing
Use
csv-parserorpapaparseto handle bulk uploads.Row-level validation: return errors per row without failing the entire batch.
Acceptance Criteria
POST /v1/payoutscreates and queues a single payoutPOST /v1/payouts/bulkaccepts CSV, validates rows, queues valid onesGET /v1/payoutsandGET /v1/payouts/:idreturn correct datapayout.completedandpayout.failed