import { Injectable } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; import { Repository } from 'typeorm'; import { Logger } from '@nestjs/common'; import { IPaymentAdapter, CreatePaymentOrderData, PaymentOrderResult, PaymentOrderQueryResult, PaymentCallbackResult, RefundRequestData, RefundResult, RefundQueryResult, BillData, PaymentMethod, PaymentOrderStatus, PaymentBusinessType, RefundStatus, } from '../interfaces/payment.interface'; import { PlatformType } from '../../entities/platform-user.entity'; import { PaymentOrderEntity, PaymentTransactionEntity, RefundRecordEntity, } from '../entities'; import { TransactionType, TransactionStatus, } from '../entities/payment-transaction.entity'; /** * 基础支付适配器抽象类 * 提供支付适配器的通用功能实现 * 包含数据库操作、订单管理、通用业务逻辑等 * 子类需要实现具体的支付平台接口调用 */ @Injectable() export abstract class BasePaymentAdapter implements IPaymentAdapter { protected readonly logger = new Logger(this.constructor.name); protected isConfigured: boolean = false; abstract platform: PlatformType; abstract paymentMethod: PaymentMethod; constructor( protected readonly httpService: HttpService, protected readonly configService: ConfigService, protected readonly paymentOrderRepository: Repository, protected readonly paymentTransactionRepository: Repository, protected readonly refundRecordRepository: Repository, ) {} /** * 创建支付订单号 * 生成唯一的系统订单号 */ protected generateOrderNo(): string { const timestamp = Date.now(); const random = Math.random().toString(36).substr(2, 9); let platformPrefix: string; switch (this.platform) { case PlatformType.WECHAT: platformPrefix = 'WX'; break; case PlatformType.BYTEDANCE: platformPrefix = 'DY'; break; case PlatformType.STRIPE: platformPrefix = 'ST'; break; default: platformPrefix = 'UN'; // Unknown } return `${platformPrefix}${timestamp}${random.toUpperCase()}`; } /** * 创建退款单号 * 生成唯一的退款单号 */ protected generateRefundNo(): string { const timestamp = Date.now(); const random = Math.random().toString(36).substr(2, 9); let platformPrefix: string; switch (this.platform) { case PlatformType.WECHAT: platformPrefix = 'WXR'; break; case PlatformType.BYTEDANCE: platformPrefix = 'DYR'; break; case PlatformType.STRIPE: platformPrefix = 'STR'; break; default: platformPrefix = 'UNR'; // Unknown Refund } return `${platformPrefix}${timestamp}${random.toUpperCase()}`; } /** * 保存支付订单到数据库 * @param orderData 订单数据 * @param thirdPartyOrderId 第三方订单ID * @param paymentParams 支付参数 * @returns 支付订单实体 */ protected async savePaymentOrder( orderData: CreatePaymentOrderData, thirdPartyOrderId?: string, paymentParams?: any, ): Promise { const paymentOrder = new PaymentOrderEntity(); paymentOrder.userId = orderData.userId; paymentOrder.platform = this.platform; paymentOrder.orderNo = orderData.orderNo; paymentOrder.thirdPartyOrderId = thirdPartyOrderId; paymentOrder.paymentMethod = this.paymentMethod; paymentOrder.status = PaymentOrderStatus.PENDING; paymentOrder.businessType = orderData.businessType; paymentOrder.businessId = orderData.businessId; paymentOrder.description = orderData.description; paymentOrder.amount = orderData.amount; paymentOrder.currency = orderData.currency; paymentOrder.platformUserId = orderData.platformUserId; paymentOrder.paymentParams = paymentParams; paymentOrder.notifyUrl = orderData.notifyUrl; paymentOrder.metadata = orderData.metadata; // 设置过期时间 if (orderData.expireMinutes) { const expiredAt = new Date(); expiredAt.setMinutes(expiredAt.getMinutes() + orderData.expireMinutes); paymentOrder.expiredAt = expiredAt; } return await this.paymentOrderRepository.save(paymentOrder); } /** * 更新支付订单状态 * @param orderId 订单ID * @param status 新状态 * @param paidAmount 支付金额 * @param paidAt 支付时间 * @param errorMessage 错误信息 */ protected async updatePaymentOrderStatus( orderId: string, status: PaymentOrderStatus, paidAmount?: number, paidAt?: Date, errorMessage?: string, ): Promise { const updateData: Partial = { status }; if (paidAmount !== undefined) { updateData.paidAmount = paidAmount; } if (paidAt) { updateData.paidAt = paidAt; } if (errorMessage) { updateData.errorMessage = errorMessage; } await this.paymentOrderRepository.update(orderId, updateData); } /** * 保存支付交易记录 * @param paymentOrderId 支付订单ID * @param transactionType 交易类型 * @param amount 交易金额 * @param requestData 请求数据 * @param responseData 响应数据 * @param status 交易状态 * @returns 交易记录实体 */ protected async savePaymentTransaction( paymentOrderId: string, transactionType: TransactionType, amount: number, requestData?: any, responseData?: any, status: TransactionStatus = TransactionStatus.PENDING, ): Promise { const transaction = new PaymentTransactionEntity(); transaction.paymentOrderId = paymentOrderId; transaction.transactionType = transactionType; transaction.status = status; transaction.amount = amount; transaction.currency = 'CNY'; transaction.requestData = requestData; transaction.responseData = responseData; return await this.paymentTransactionRepository.save(transaction); } /** * 更新支付交易记录 * @param transactionId 交易记录ID * @param status 交易状态 * @param thirdPartyTransactionId 第三方交易号 * @param responseData 响应数据 * @param errorCode 错误代码 * @param errorMessage 错误信息 */ protected async updatePaymentTransaction( transactionId: string, status: TransactionStatus, thirdPartyTransactionId?: string, responseData?: any, errorCode?: string, errorMessage?: string, ): Promise { const updateData: Partial = { status, lastProcessedAt: new Date(), }; if (thirdPartyTransactionId) { updateData.thirdPartyTransactionId = thirdPartyTransactionId; } if (responseData) { updateData.responseData = responseData; } if (errorCode) { updateData.errorCode = errorCode; } if (errorMessage) { updateData.errorMessage = errorMessage; } await this.paymentTransactionRepository.update(transactionId, updateData); } /** * 保存退款记录 * @param refundData 退款数据 * @param paymentOrder 原支付订单 * @returns 退款记录实体 */ protected async saveRefundRecord( refundData: RefundRequestData, paymentOrder: PaymentOrderEntity, ): Promise { const refundRecord = new RefundRecordEntity(); refundRecord.paymentOrderId = refundData.orderId; refundRecord.refundNo = refundData.refundNo; refundRecord.thirdPartyTransactionId = refundData.thirdPartyTransactionId; refundRecord.status = RefundStatus.PENDING; refundRecord.refundAmount = refundData.refundAmount; refundRecord.originalAmount = paymentOrder.paidAmount || paymentOrder.amount; refundRecord.currency = paymentOrder.currency; refundRecord.reason = refundData.reason; refundRecord.requestedBy = paymentOrder.userId; return await this.refundRecordRepository.save(refundRecord); } /** * 更新退款记录状态 * @param refundId 退款记录ID * @param status 退款状态 * @param thirdPartyRefundId 第三方退款ID * @param refundedAt 退款时间 * @param errorCode 错误代码 * @param failureReason 失败原因 */ protected async updateRefundRecord( refundId: string, status: RefundStatus, thirdPartyRefundId?: string, refundedAt?: Date, errorCode?: string, failureReason?: string, ): Promise { const updateData: Partial = { status, lastProcessedAt: new Date(), }; if (thirdPartyRefundId) { updateData.thirdPartyRefundId = thirdPartyRefundId; } if (refundedAt) { updateData.refundedAt = refundedAt; } if (errorCode) { updateData.errorCode = errorCode; } if (failureReason) { updateData.failureReason = failureReason; } await this.refundRecordRepository.update(refundId, updateData); } /** * 统一错误处理 * @param error 错误对象 * @param context 错误上下文 * @returns 标准化错误 */ protected handleError(error: any, context: string): Error { this.logger.error(`${context} 错误:`, error); if (error.response?.data) { const errorData = error.response.data; return new Error( `${this.platform}支付错误: ${errorData.message || errorData.err_tips || errorData.errmsg || '未知错误'}`, ); } return new Error(`${this.platform}支付${context}失败: ${error.message}`); } /** * 验证订单金额 * @param amount 金额(分) * @returns 是否有效 */ protected validateAmount(amount: number): boolean { return amount > 0 && amount <= 100000000; // 最大1000万分(10万元) } /** * 验证货币类型 * @param currency 货币类型 * @returns 是否有效 */ protected validateCurrency(currency: string): boolean { return ['CNY', 'USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'SGD', 'HKD'].includes(currency); } /** * 检查订单是否可以操作 * @param paymentOrder 支付订单 * @param operation 操作类型 * @returns 检查结果 */ protected validateOrderOperation( paymentOrder: PaymentOrderEntity, operation: 'pay' | 'refund' | 'query', ): { valid: boolean; message?: string } { if (!paymentOrder) { return { valid: false, message: '订单不存在' }; } switch (operation) { case 'pay': if (!paymentOrder.canPay()) { return { valid: false, message: '订单不能支付' }; } break; case 'refund': if (!paymentOrder.canRefund()) { return { valid: false, message: '订单不能退款' }; } break; case 'query': // 查询操作不需要特殊验证 break; default: return { valid: false, message: '不支持的操作类型' }; } return { valid: true }; } // 抽象方法 - 子类必须实现 abstract createPaymentOrder( orderData: CreatePaymentOrderData, ): Promise; abstract queryPaymentOrder( orderId: string, thirdPartyOrderId?: string, ): Promise; abstract handlePaymentCallback( callbackData: any, ): Promise; abstract refundPayment(refundData: RefundRequestData): Promise; abstract queryRefundStatus(refundId: string): Promise; abstract verifySignature( callbackData: any, signature: string, ): Promise; abstract downloadBill(date: string): Promise; }