import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Payout } from './entities/payout.entity'; import { WalletService } from '../wallet/wallet.service'; export interface PayoutRequestDto { amount: number; channel: string; // bank | cliq | mtn | ecash | syriatel currency?: string; destination?: Record; } /** * سحب أرباح السائق: يحجز المبلغ من المحفظة فوراً (debit)، ثم يُحوَّل خارجياً. * الفشل يُعيد المبلغ للمحفظة. التحويل الخارجي الفعلي يُنفَّذ يدوياً/بمزوّد لاحقاً. */ @Injectable() export class PayoutsService { constructor( @InjectRepository(Payout) private readonly repo: Repository, private readonly wallet: WalletService, ) {} async request(tenantId: string, driverUserId: string, dto: PayoutRequestDto) { const amount = Number(dto.amount); if (!(amount > 0)) throw new BadRequestException('amount must be > 0'); if (!dto.channel) throw new BadRequestException('channel is required'); // يحجز المبلغ (يرمي لو الرصيد غير كافٍ) await this.wallet.debit(tenantId, driverUserId, amount, 'payout_hold'); return this.repo.save( this.repo.create({ tenant_id: tenantId, driver_user_id: driverUserId, amount, currency: dto.currency ?? 'JOD', channel: dto.channel, destination: dto.destination ?? {}, status: 'requested', }), ); } listMine(tenantId: string, driverUserId: string) { return this.repo.find({ where: { tenant_id: tenantId, driver_user_id: driverUserId }, order: { created_at: 'DESC' }, }); } private async getOr404(tenantId: string, id: string): Promise { const p = await this.repo.findOne({ where: { tenant_id: tenantId, id } }); if (!p) throw new NotFoundException('payout not found'); return p; } /** الأدمن يؤكّد أن المبلغ حُوِّل خارجياً. */ async complete(tenantId: string, id: string, ref?: string) { const p = await this.getOr404(tenantId, id); if (p.status === 'paid') return p; p.status = 'paid'; p.ref = ref ?? p.ref; return this.repo.save(p); } /** فشل التحويل — يُعاد المبلغ للمحفظة. */ async fail(tenantId: string, id: string) { const p = await this.getOr404(tenantId, id); if (p.status === 'paid') throw new BadRequestException('already paid'); if (p.status !== 'failed') { await this.wallet.credit(tenantId, p.driver_user_id, Number(p.amount), 'payout_refund', p.id); } p.status = 'failed'; return this.repo.save(p); } }