import { Injectable, Inject, NotFoundException, BadRequestException } from '@nestjs/common'; import { PaymentEntity, PaymentMethod, PaymentStatus } from '../domain/entities/payment.entity'; import { OrderStatus } from '../domain/entities/order.entity'; import { IPaymentRepository, PAYMENT_REPOSITORY } from '../domain/repositories/payment.repository.interface'; import { OrderService } from '../order/order.service'; import { AlipayAdapter } from './adapters/alipay.adapter'; import { WechatPayAdapter } from './adapters/wechat-pay.adapter'; import { StripeAdapter } from './adapters/stripe.adapter'; export interface CreatePaymentDto { orderId: string; method: PaymentMethod; } export interface PaymentResult { paymentId: string; orderId: string; qrCodeUrl?: string; paymentUrl?: string; expiresAt: Date; method: PaymentMethod; amount: number; } @Injectable() export class PaymentService { constructor( @Inject(PAYMENT_REPOSITORY) private readonly paymentRepo: IPaymentRepository, private readonly orderService: OrderService, private readonly alipayAdapter: AlipayAdapter, private readonly wechatPayAdapter: WechatPayAdapter, private readonly stripeAdapter: StripeAdapter, ) {} async createPayment(dto: CreatePaymentDto): Promise { const order = await this.orderService.findById(dto.orderId); if (!order.canBePaid()) { throw new BadRequestException('Cannot create payment for this order'); } // Check for existing pending payment const existingPayment = await this.paymentRepo.findPendingByOrderId(dto.orderId); if (existingPayment && !existingPayment.isExpired()) { return { paymentId: existingPayment.id, orderId: existingPayment.orderId, qrCodeUrl: existingPayment.qrCodeUrl || undefined, paymentUrl: existingPayment.paymentUrl || undefined, expiresAt: existingPayment.expiresAt, method: existingPayment.method, amount: existingPayment.amount, }; } // Create payment via adapter let qrCodeUrl: string | undefined; let paymentUrl: string | undefined; switch (dto.method) { case PaymentMethod.ALIPAY: const alipayResult = await this.alipayAdapter.createPayment(order); qrCodeUrl = alipayResult.qrCodeUrl; break; case PaymentMethod.WECHAT: const wechatResult = await this.wechatPayAdapter.createPayment(order); qrCodeUrl = wechatResult.qrCodeUrl; break; case PaymentMethod.CREDIT_CARD: const stripeResult = await this.stripeAdapter.createPayment(order); paymentUrl = stripeResult.paymentUrl; break; default: throw new BadRequestException('Unsupported payment method'); } // Create payment entity const payment = PaymentEntity.create({ orderId: order.id, method: dto.method, amount: order.amount, currency: order.currency, qrCodeUrl, paymentUrl, }); const savedPayment = await this.paymentRepo.save(payment); // Update order status await this.orderService.updateStatus(order.id, OrderStatus.PENDING_PAYMENT); return { paymentId: savedPayment.id, orderId: savedPayment.orderId, qrCodeUrl: savedPayment.qrCodeUrl || undefined, paymentUrl: savedPayment.paymentUrl || undefined, expiresAt: savedPayment.expiresAt, method: savedPayment.method, amount: savedPayment.amount, }; } async findById(paymentId: string): Promise { const payment = await this.paymentRepo.findById(paymentId); if (!payment) { throw new NotFoundException('Payment not found'); } return payment; } async handleCallback( method: PaymentMethod, payload: Record, ): Promise { let orderId: string; let transactionId: string; let success: boolean; switch (method) { case PaymentMethod.ALIPAY: const alipayResult = await this.alipayAdapter.handleCallback(payload); orderId = alipayResult.orderId; transactionId = alipayResult.transactionId; success = alipayResult.success; break; case PaymentMethod.WECHAT: const wechatResult = await this.wechatPayAdapter.handleCallback(payload); orderId = wechatResult.orderId; transactionId = wechatResult.transactionId; success = wechatResult.success; break; case PaymentMethod.CREDIT_CARD: const stripeResult = await this.stripeAdapter.handleCallback(payload); orderId = stripeResult.orderId; transactionId = stripeResult.transactionId; success = stripeResult.success; break; default: throw new BadRequestException('Unsupported payment method'); } // Find payment by order ID const payment = await this.paymentRepo.findPendingByOrderId(orderId); if (!payment) { throw new NotFoundException('Payment not found'); } // Update payment if (success) { payment.markAsCompleted(transactionId, payload); await this.paymentRepo.update(payment); // Update order await this.orderService.markAsPaid(orderId, payment.id, method); } else { payment.markAsFailed('Payment failed', payload); await this.paymentRepo.update(payment); } } async checkStatus(paymentId: string): Promise<{ status: PaymentStatus; paidAt?: Date }> { const payment = await this.findById(paymentId); return { status: payment.status, paidAt: payment.paidAt || undefined, }; } }