- 新增Google OAuth 2.0认证模块 - 实现Google Strategy和AuthController - 添加Google平台适配器和用户统一管理 - 支持OAuth回调和token交换机制 - 完善Stripe支付集成 - 新增Stripe支付适配器和控制器 - 实现webhook事件处理和支付确认 - 支持多币种和国际化支付 - 扩展平台类型支持 - 在PlatformType中新增GOOGLE和STRIPE - 更新适配器工厂注册机制 - 完善跨平台用户身份管理 - 增强依赖和配置 - 添加@nestjs/passport和passport-google-oauth20 - 更新支付方法枚举和货币支持 - 完善订单号生成和回调URL构建
790 lines
22 KiB
TypeScript
790 lines
22 KiB
TypeScript
import {
|
||
Injectable,
|
||
Logger,
|
||
BadRequestException,
|
||
NotFoundException,
|
||
} from '@nestjs/common';
|
||
import { InjectRepository } from '@nestjs/typeorm';
|
||
import { Repository } from 'typeorm';
|
||
import { ConfigService } from '@nestjs/config';
|
||
import {
|
||
CreatePaymentOrderData,
|
||
PaymentOrderResult,
|
||
PaymentOrderQueryResult,
|
||
PaymentCallbackResult,
|
||
RefundRequestData,
|
||
RefundResult,
|
||
RefundQueryResult,
|
||
PaymentMethod,
|
||
PaymentBusinessType,
|
||
PaymentOrderStatus,
|
||
} from '../interfaces/payment.interface';
|
||
import { PlatformType } from '../../entities/platform-user.entity';
|
||
import { UserEntity } from '../../entities/user.entity';
|
||
import {
|
||
UserSubscriptionEntity,
|
||
SubscriptionPlan,
|
||
SubscriptionStatus,
|
||
} from '../../entities/user-subscription.entity';
|
||
import {
|
||
UserCreditEntity,
|
||
CreditType,
|
||
CreditSource,
|
||
} from '../../entities/user-credit.entity';
|
||
import {
|
||
PaymentOrderEntity,
|
||
PaymentTransactionEntity,
|
||
RefundRecordEntity,
|
||
} from '../entities';
|
||
import { PaymentAdapterFactory } from './payment-adapter.factory';
|
||
|
||
/**
|
||
* 业务配置接口
|
||
* 定义不同业务类型的配置信息
|
||
*/
|
||
interface BusinessConfig {
|
||
/** 业务名称 */
|
||
name: string;
|
||
/** 单价(分) */
|
||
unitPrice: number;
|
||
/** 描述 */
|
||
description: string;
|
||
/** 是否启用 */
|
||
enabled: boolean;
|
||
}
|
||
|
||
/**
|
||
* 订阅套餐配置
|
||
*/
|
||
interface SubscriptionConfig extends BusinessConfig {
|
||
/** 套餐类型 */
|
||
plan: SubscriptionPlan;
|
||
/** 有效期(天) */
|
||
validDays: number;
|
||
/** 每月积分 */
|
||
monthlyCredits: number;
|
||
/** 特权功能 */
|
||
features: string[];
|
||
}
|
||
|
||
/**
|
||
* 积分包配置
|
||
*/
|
||
interface CreditPackageConfig extends BusinessConfig {
|
||
/** 积分数量 */
|
||
credits: number;
|
||
/** 赠送积分 */
|
||
bonusCredits: number;
|
||
}
|
||
|
||
/**
|
||
* 统一支付服务
|
||
* 提供跨平台支付的统一业务接口
|
||
* 集成订阅服务、积分购买等业务逻辑
|
||
* 管理支付订单的完整生命周期
|
||
*/
|
||
@Injectable()
|
||
export class UnifiedPaymentService {
|
||
private readonly logger = new Logger(UnifiedPaymentService.name);
|
||
|
||
constructor(
|
||
private readonly paymentAdapterFactory: PaymentAdapterFactory,
|
||
private readonly configService: ConfigService,
|
||
@InjectRepository(UserEntity)
|
||
private readonly userRepository: Repository<UserEntity>,
|
||
@InjectRepository(UserSubscriptionEntity)
|
||
private readonly subscriptionRepository: Repository<UserSubscriptionEntity>,
|
||
@InjectRepository(UserCreditEntity)
|
||
private readonly creditRepository: Repository<UserCreditEntity>,
|
||
@InjectRepository(PaymentOrderEntity)
|
||
private readonly paymentOrderRepository: Repository<PaymentOrderEntity>,
|
||
) {}
|
||
|
||
/**
|
||
* 创建支付订单
|
||
* 根据业务类型和平台创建相应的支付订单
|
||
* @param platform 支付平台
|
||
* @param userId 用户ID
|
||
* @param businessType 业务类型
|
||
* @param businessId 业务ID
|
||
* @param platformUserId 平台用户ID
|
||
* @param currency 货币类型(可选,默认CNY)
|
||
* @param customAmount 自定义金额(可选,覆盖配置价格)
|
||
* @returns 支付订单结果
|
||
*/
|
||
async createPaymentOrder(
|
||
platform: PlatformType,
|
||
userId: string,
|
||
businessType: PaymentBusinessType,
|
||
businessId: string,
|
||
platformUserId: string,
|
||
currency: string = 'CNY',
|
||
customAmount?: number,
|
||
): Promise<PaymentOrderResult> {
|
||
try {
|
||
this.logger.log(
|
||
`创建支付订单: 用户=${userId}, 业务=${businessType}, 业务ID=${businessId}`,
|
||
);
|
||
|
||
// 1. 验证用户存在
|
||
const user = await this.userRepository.findOne({ where: { id: userId } });
|
||
if (!user) {
|
||
throw new NotFoundException('用户不存在');
|
||
}
|
||
|
||
// 2. 获取业务配置
|
||
const businessConfig = await this.getBusinessConfig(
|
||
businessType,
|
||
businessId,
|
||
);
|
||
if (!businessConfig.enabled) {
|
||
throw new BadRequestException('该商品暂不可购买');
|
||
}
|
||
|
||
// 3. 业务前置检查
|
||
await this.validateBusinessOrder(userId, businessType, businessId);
|
||
|
||
// 4. 确定支付方式
|
||
const paymentMethod = this.determinePaymentMethod(platform);
|
||
|
||
// 5. 构建订单数据
|
||
const orderData: CreatePaymentOrderData = {
|
||
orderNo: '', // 由适配器生成
|
||
amount: customAmount || businessConfig.unitPrice,
|
||
currency: currency,
|
||
description: businessConfig.description,
|
||
userId,
|
||
platformUserId,
|
||
businessType,
|
||
businessId,
|
||
expireMinutes: 30, // 默认30分钟过期
|
||
metadata: {
|
||
businessName: businessConfig.name,
|
||
platform,
|
||
currency,
|
||
},
|
||
notifyUrl: this.buildNotifyUrl(platform),
|
||
};
|
||
|
||
// 6. 获取支付适配器并创建订单
|
||
const adapter = this.paymentAdapterFactory.getAdapter(
|
||
platform,
|
||
paymentMethod,
|
||
);
|
||
console.log(orderData)
|
||
const result = await adapter.createPaymentOrder(orderData);
|
||
|
||
this.logger.log(
|
||
`支付订单创建成功: 订单ID=${result.orderId}, 第三方订单ID=${result.thirdPartyOrderId}`,
|
||
);
|
||
return result;
|
||
} catch (error) {
|
||
this.logger.error(`创建支付订单失败: ${error.message}`, error.stack);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询支付订单状态
|
||
* @param orderId 订单ID
|
||
* @returns 订单查询结果
|
||
*/
|
||
async queryPaymentOrder(orderId: string): Promise<PaymentOrderQueryResult> {
|
||
try {
|
||
// 1. 获取本地订单信息
|
||
const paymentOrder = await this.paymentOrderRepository.findOne({
|
||
where: { id: orderId },
|
||
});
|
||
|
||
if (!paymentOrder) {
|
||
throw new NotFoundException('支付订单不存在');
|
||
}
|
||
|
||
// 2. 获取支付适配器
|
||
const adapter = this.paymentAdapterFactory.getAdapter(
|
||
paymentOrder.platform,
|
||
paymentOrder.paymentMethod,
|
||
);
|
||
|
||
// 3. 查询订单状态
|
||
const result = await adapter.queryPaymentOrder(
|
||
orderId,
|
||
paymentOrder.thirdPartyOrderId,
|
||
);
|
||
|
||
// 4. 如果订单状态变为已支付,触发业务处理
|
||
if (
|
||
result.status === PaymentOrderStatus.PAID &&
|
||
paymentOrder.status !== PaymentOrderStatus.PAID
|
||
) {
|
||
await this.handlePaymentSuccess(paymentOrder, result);
|
||
}
|
||
|
||
return result;
|
||
} catch (error) {
|
||
this.logger.error(`查询支付订单失败: ${error.message}`, error.stack);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理支付回调
|
||
* @param platform 支付平台
|
||
* @param callbackData 回调数据
|
||
* @returns 回调处理结果
|
||
*/
|
||
async handlePaymentCallback(
|
||
platform: PlatformType,
|
||
callbackData: any,
|
||
): Promise<PaymentCallbackResult> {
|
||
try {
|
||
this.logger.log(`处理${platform}支付回调:`, JSON.stringify(callbackData));
|
||
|
||
// 1. 确定支付方式
|
||
const paymentMethod = this.determinePaymentMethod(platform);
|
||
|
||
// 2. 获取支付适配器
|
||
const adapter = this.paymentAdapterFactory.getAdapter(
|
||
platform,
|
||
paymentMethod,
|
||
);
|
||
|
||
// 3. 处理回调
|
||
const result = await adapter.handlePaymentCallback(callbackData);
|
||
|
||
// 4. 如果支付成功,触发业务处理
|
||
if (
|
||
result.success &&
|
||
result.status === PaymentOrderStatus.PAID &&
|
||
result.orderId
|
||
) {
|
||
const paymentOrder = await this.paymentOrderRepository.findOne({
|
||
where: { id: result.orderId },
|
||
});
|
||
|
||
if (paymentOrder) {
|
||
await this.handlePaymentSuccess(paymentOrder, result);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} catch (error) {
|
||
this.logger.error(`处理支付回调失败: ${error.message}`, error.stack);
|
||
return {
|
||
success: false,
|
||
orderId: '',
|
||
status: PaymentOrderStatus.FAILED,
|
||
errorMessage: error.message,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 申请退款
|
||
* @param orderId 订单ID
|
||
* @param refundAmount 退款金额
|
||
* @param reason 退款原因
|
||
* @returns 退款结果
|
||
*/
|
||
async refundPayment(
|
||
orderId: string,
|
||
refundAmount: number,
|
||
reason: string,
|
||
): Promise<RefundResult> {
|
||
try {
|
||
// 1. 获取原订单信息
|
||
const paymentOrder = await this.paymentOrderRepository.findOne({
|
||
where: { id: orderId },
|
||
});
|
||
|
||
if (!paymentOrder) {
|
||
throw new NotFoundException('支付订单不存在');
|
||
}
|
||
|
||
// 2. 验证退款条件
|
||
if (!paymentOrder.canRefund()) {
|
||
throw new BadRequestException('订单不支持退款');
|
||
}
|
||
|
||
const refundableAmount = paymentOrder.getRefundableAmount();
|
||
if (refundAmount > refundableAmount) {
|
||
throw new BadRequestException(
|
||
`退款金额不能超过可退金额 ${refundableAmount / 100} 元`,
|
||
);
|
||
}
|
||
|
||
// 3. 获取支付适配器
|
||
const adapter = this.paymentAdapterFactory.getAdapter(
|
||
paymentOrder.platform,
|
||
paymentOrder.paymentMethod,
|
||
);
|
||
|
||
// 4. 构建退款数据
|
||
const refundData: RefundRequestData = {
|
||
orderId,
|
||
refundAmount,
|
||
reason,
|
||
refundNo: this.generateRefundNo(),
|
||
thirdPartyOrderId: paymentOrder.thirdPartyOrderId || '',
|
||
thirdPartyTransactionId: '', // 需要从交易记录中获取
|
||
};
|
||
|
||
// 5. 申请退款
|
||
const result = await adapter.refundPayment(refundData);
|
||
|
||
this.logger.log(
|
||
`退款申请成功: 退款ID=${result.refundId}, 金额=${refundAmount / 100}元`,
|
||
);
|
||
return result;
|
||
} catch (error) {
|
||
this.logger.error(`申请退款失败: ${error.message}`, error.stack);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询退款状态
|
||
* @param refundId 退款ID
|
||
* @returns 退款查询结果
|
||
*/
|
||
async queryRefundStatus(refundId: string): Promise<RefundQueryResult> {
|
||
try {
|
||
// 1. 获取退款记录
|
||
const refundRecord = await this.paymentOrderRepository
|
||
.createQueryBuilder('order')
|
||
.leftJoinAndSelect('order.refunds', 'refund')
|
||
.where('refund.id = :refundId', { refundId })
|
||
.getOne();
|
||
|
||
if (!refundRecord) {
|
||
throw new NotFoundException('退款记录不存在');
|
||
}
|
||
|
||
const refund = refundRecord.refunds[0];
|
||
const paymentOrder = refundRecord;
|
||
|
||
// 2. 获取支付适配器
|
||
const adapter = this.paymentAdapterFactory.getAdapter(
|
||
paymentOrder.platform,
|
||
paymentOrder.paymentMethod,
|
||
);
|
||
|
||
// 3. 查询退款状态
|
||
return await adapter.queryRefundStatus(refundId);
|
||
} catch (error) {
|
||
this.logger.error(`查询退款状态失败: ${error.message}`, error.stack);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户支付订单列表
|
||
* @param userId 用户ID
|
||
* @param page 页码
|
||
* @param limit 每页数量
|
||
* @returns 订单列表
|
||
*/
|
||
async getUserPaymentOrders(
|
||
userId: string,
|
||
page: number = 1,
|
||
limit: number = 20,
|
||
): Promise<{
|
||
orders: PaymentOrderEntity[];
|
||
total: number;
|
||
page: number;
|
||
limit: number;
|
||
}> {
|
||
const [orders, total] = await this.paymentOrderRepository.findAndCount({
|
||
where: { userId },
|
||
order: { createdAt: 'DESC' },
|
||
skip: (page - 1) * limit,
|
||
take: limit,
|
||
relations: ['transactions', 'refunds'],
|
||
});
|
||
|
||
return { orders, total, page, limit };
|
||
}
|
||
|
||
/**
|
||
* 处理支付成功的业务逻辑
|
||
* @param paymentOrder 支付订单
|
||
* @param paymentResult 支付结果
|
||
*/
|
||
private async handlePaymentSuccess(
|
||
paymentOrder: PaymentOrderEntity,
|
||
paymentResult: PaymentOrderQueryResult | PaymentCallbackResult,
|
||
): Promise<void> {
|
||
try {
|
||
this.logger.log(
|
||
`处理支付成功业务: 订单ID=${paymentOrder.id}, 业务类型=${paymentOrder.businessType}`,
|
||
);
|
||
|
||
switch (paymentOrder.businessType) {
|
||
case PaymentBusinessType.SUBSCRIPTION:
|
||
await this.handleSubscriptionPayment(paymentOrder);
|
||
break;
|
||
case PaymentBusinessType.CREDIT_PURCHASE:
|
||
await this.handleCreditPurchasePayment(paymentOrder);
|
||
break;
|
||
case PaymentBusinessType.TEMPLATE_UNLOCK:
|
||
await this.handleTemplateUnlockPayment(paymentOrder);
|
||
break;
|
||
default:
|
||
this.logger.warn(`未知的业务类型: ${paymentOrder.businessType}`);
|
||
}
|
||
|
||
this.logger.log(`支付成功业务处理完成: 订单ID=${paymentOrder.id}`);
|
||
} catch (error) {
|
||
this.logger.error(`处理支付成功业务失败: ${error.message}`, error.stack);
|
||
// 业务处理失败不应该影响支付状态,但需要记录日志并可能需要人工处理
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理订阅支付
|
||
* @param paymentOrder 支付订单
|
||
*/
|
||
private async handleSubscriptionPayment(
|
||
paymentOrder: PaymentOrderEntity,
|
||
): Promise<void> {
|
||
const config = await this.getSubscriptionConfig(paymentOrder.businessId);
|
||
|
||
// 创建或更新订阅
|
||
const subscription = new UserSubscriptionEntity();
|
||
subscription.userId = paymentOrder.userId;
|
||
subscription.platform = paymentOrder.platform;
|
||
subscription.plan = config.plan;
|
||
subscription.status = SubscriptionStatus.ACTIVE;
|
||
subscription.startDate = new Date();
|
||
subscription.endDate = new Date(
|
||
Date.now() + config.validDays * 24 * 60 * 60 * 1000,
|
||
);
|
||
subscription.price = paymentOrder.amount / 100; // 转换为元
|
||
subscription.monthlyCredits = config.monthlyCredits;
|
||
subscription.features = config.features;
|
||
subscription.paymentId = paymentOrder.id;
|
||
|
||
await this.subscriptionRepository.save(subscription);
|
||
|
||
// 发放订阅积分
|
||
if (config.monthlyCredits > 0) {
|
||
await this.addUserCredits(
|
||
paymentOrder.userId,
|
||
paymentOrder.platform,
|
||
config.monthlyCredits,
|
||
CreditType.REWARD,
|
||
CreditSource.SUBSCRIPTION,
|
||
`订阅${config.name}获得积分`,
|
||
paymentOrder.id,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理积分购买支付
|
||
* @param paymentOrder 支付订单
|
||
*/
|
||
private async handleCreditPurchasePayment(
|
||
paymentOrder: PaymentOrderEntity,
|
||
): Promise<void> {
|
||
const config = await this.getCreditPackageConfig(paymentOrder.businessId);
|
||
|
||
// 发放积分
|
||
const totalCredits = config.credits + config.bonusCredits;
|
||
await this.addUserCredits(
|
||
paymentOrder.userId,
|
||
paymentOrder.platform,
|
||
totalCredits,
|
||
CreditType.PURCHASE,
|
||
CreditSource.MANUAL,
|
||
`购买${config.name}获得积分`,
|
||
paymentOrder.id,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 处理模板解锁支付
|
||
* @param paymentOrder 支付订单
|
||
*/
|
||
private async handleTemplateUnlockPayment(
|
||
paymentOrder: PaymentOrderEntity,
|
||
): Promise<void> {
|
||
// 模板解锁逻辑,可能需要更新用户的模板权限
|
||
// 这里需要根据具体业务逻辑实现
|
||
this.logger.log(
|
||
`模板解锁支付处理: 用户=${paymentOrder.userId}, 模板=${paymentOrder.businessId}`,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 添加用户积分
|
||
* @param userId 用户ID
|
||
* @param platform 平台
|
||
* @param amount 积分数量
|
||
* @param type 积分类型
|
||
* @param source 积分来源
|
||
* @param description 描述
|
||
* @param referenceId 关联ID
|
||
*/
|
||
private async addUserCredits(
|
||
userId: string,
|
||
platform: PlatformType,
|
||
amount: number,
|
||
type: CreditType,
|
||
source: CreditSource,
|
||
description: string,
|
||
referenceId: string,
|
||
): Promise<void> {
|
||
// 计算新余额
|
||
const lastCredit = await this.creditRepository.findOne({
|
||
where: { userId },
|
||
order: { createdAt: 'DESC' },
|
||
});
|
||
|
||
const newBalance = (lastCredit?.balance || 0) + amount;
|
||
|
||
// 创建积分记录
|
||
const credit = new UserCreditEntity();
|
||
credit.userId = userId;
|
||
credit.platform = platform;
|
||
credit.type = type;
|
||
credit.source = source;
|
||
credit.amount = amount;
|
||
credit.balance = newBalance;
|
||
credit.description = description;
|
||
credit.referenceId = referenceId;
|
||
|
||
await this.creditRepository.save(credit);
|
||
}
|
||
|
||
/**
|
||
* 业务前置检查
|
||
* @param userId 用户ID
|
||
* @param businessType 业务类型
|
||
* @param businessId 业务ID
|
||
*/
|
||
private async validateBusinessOrder(
|
||
userId: string,
|
||
businessType: PaymentBusinessType,
|
||
businessId: string,
|
||
): Promise<void> {
|
||
switch (businessType) {
|
||
case PaymentBusinessType.SUBSCRIPTION:
|
||
await this.validateSubscriptionOrder(userId, businessId);
|
||
break;
|
||
case PaymentBusinessType.CREDIT_PURCHASE:
|
||
// 积分购买一般无特殊限制
|
||
break;
|
||
case PaymentBusinessType.TEMPLATE_UNLOCK:
|
||
await this.validateTemplateUnlockOrder(userId, businessId);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证订阅订单
|
||
*/
|
||
private async validateSubscriptionOrder(
|
||
userId: string,
|
||
businessId: string,
|
||
): Promise<void> {
|
||
// 检查用户是否已有有效订阅
|
||
const existingSubscription = await this.subscriptionRepository.findOne({
|
||
where: {
|
||
userId,
|
||
status: SubscriptionStatus.ACTIVE,
|
||
},
|
||
});
|
||
|
||
if (existingSubscription) {
|
||
throw new BadRequestException('您已有有效订阅,请等待到期后再次购买');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证模板解锁订单
|
||
*/
|
||
private async validateTemplateUnlockOrder(
|
||
userId: string,
|
||
templateId: string,
|
||
): Promise<void> {
|
||
// 检查用户是否已解锁该模板
|
||
// 这里需要根据具体业务逻辑实现
|
||
}
|
||
|
||
/**
|
||
* 确定支付方式
|
||
* @param platform 平台类型
|
||
* @returns 支付方式
|
||
*/
|
||
private determinePaymentMethod(platform: PlatformType): PaymentMethod {
|
||
switch (platform) {
|
||
case PlatformType.WECHAT:
|
||
return PaymentMethod.WECHAT_MINIPROGRAM;
|
||
case PlatformType.BYTEDANCE:
|
||
return PaymentMethod.BYTEDANCE_MINIPROGRAM;
|
||
case PlatformType.STRIPE:
|
||
return PaymentMethod.STRIPE_CARD;
|
||
default:
|
||
throw new BadRequestException('不支持的支付平台');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取业务配置
|
||
* @param businessType 业务类型
|
||
* @param businessId 业务ID
|
||
* @returns 业务配置
|
||
*/
|
||
private async getBusinessConfig(
|
||
businessType: PaymentBusinessType,
|
||
businessId: string,
|
||
): Promise<BusinessConfig> {
|
||
switch (businessType) {
|
||
case PaymentBusinessType.SUBSCRIPTION:
|
||
return await this.getSubscriptionConfig(businessId);
|
||
case PaymentBusinessType.CREDIT_PURCHASE:
|
||
return await this.getCreditPackageConfig(businessId);
|
||
case PaymentBusinessType.TEMPLATE_UNLOCK:
|
||
return await this.getTemplateConfig(businessId);
|
||
default:
|
||
throw new BadRequestException('不支持的业务类型');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取订阅配置
|
||
*/
|
||
private async getSubscriptionConfig(
|
||
businessId: string,
|
||
): Promise<SubscriptionConfig> {
|
||
const configs: Record<string, SubscriptionConfig> = {
|
||
basic_monthly: {
|
||
name: '基础版月度订阅',
|
||
plan: SubscriptionPlan.BASIC,
|
||
unitPrice: 1980, // 19.8元
|
||
description: '基础版会员月度订阅',
|
||
validDays: 30,
|
||
monthlyCredits: 1000,
|
||
features: ['basic_templates', 'standard_generation'],
|
||
enabled: true,
|
||
},
|
||
premium_monthly: {
|
||
name: '高级版月度订阅',
|
||
plan: SubscriptionPlan.PREMIUM,
|
||
unitPrice: 2980, // 29.8元
|
||
description: '高级版会员月度订阅',
|
||
validDays: 30,
|
||
monthlyCredits: 2000,
|
||
features: ['premium_templates', 'fast_generation', 'batch_processing'],
|
||
enabled: true,
|
||
},
|
||
pro_monthly: {
|
||
name: '专业版月度订阅',
|
||
plan: SubscriptionPlan.PRO,
|
||
unitPrice: 4980, // 49.8元
|
||
description: '专业版会员月度订阅',
|
||
validDays: 30,
|
||
monthlyCredits: 5000,
|
||
features: [
|
||
'all_templates',
|
||
'fastest_generation',
|
||
'unlimited_processing',
|
||
'priority_support',
|
||
],
|
||
enabled: true,
|
||
},
|
||
};
|
||
|
||
const config = configs[businessId];
|
||
if (!config) {
|
||
throw new BadRequestException('无效的订阅套餐ID');
|
||
}
|
||
|
||
return config;
|
||
}
|
||
|
||
/**
|
||
* 获取积分包配置
|
||
*/
|
||
private async getCreditPackageConfig(
|
||
businessId: string,
|
||
): Promise<CreditPackageConfig> {
|
||
const configs: Record<string, CreditPackageConfig> = {
|
||
credits_100: {
|
||
name: '100积分包',
|
||
unitPrice: 980, // 9.8元
|
||
description: '购买100积分',
|
||
credits: 100,
|
||
bonusCredits: 0,
|
||
enabled: true,
|
||
},
|
||
credits_500: {
|
||
name: '500积分包',
|
||
unitPrice: 4800, // 48元
|
||
description: '购买500积分',
|
||
credits: 500,
|
||
bonusCredits: 50, // 赠送50积分
|
||
enabled: true,
|
||
},
|
||
credits_1000: {
|
||
name: '1000积分包',
|
||
unitPrice: 8800, // 88元
|
||
description: '购买1000积分',
|
||
credits: 1000,
|
||
bonusCredits: 200, // 赠送200积分
|
||
enabled: true,
|
||
},
|
||
};
|
||
|
||
const config = configs[businessId];
|
||
if (!config) {
|
||
throw new BadRequestException('无效的积分包ID');
|
||
}
|
||
|
||
return config;
|
||
}
|
||
|
||
/**
|
||
* 获取模板配置
|
||
*/
|
||
private async getTemplateConfig(businessId: string): Promise<BusinessConfig> {
|
||
// 这里可以从数据库或配置文件中获取模板价格
|
||
return {
|
||
name: `模板解锁-${businessId}`,
|
||
unitPrice: 1980, // 19.8元
|
||
description: '解锁高级模板',
|
||
enabled: true,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 生成退款单号
|
||
*/
|
||
private generateRefundNo(): string {
|
||
const timestamp = Date.now();
|
||
const random = Math.random().toString(36).substr(2, 9);
|
||
return `RF${timestamp}${random.toUpperCase()}`;
|
||
}
|
||
|
||
/**
|
||
* 构建回调通知URL
|
||
* @param platform 支付平台
|
||
* @returns 回调URL
|
||
*/
|
||
private buildNotifyUrl(platform: PlatformType): string {
|
||
const baseUrl = this.configService.get('SERVER_URL') || 'http://localhost:3000';
|
||
|
||
switch (platform) {
|
||
case PlatformType.WECHAT:
|
||
return `${baseUrl}/api/v1/payment/wechat/callback`;
|
||
case PlatformType.BYTEDANCE:
|
||
return `${baseUrl}/api/v1/payment/bytedance/callback`;
|
||
case PlatformType.STRIPE:
|
||
return `${baseUrl}/api/v1/payment/stripe/webhook`;
|
||
default:
|
||
return `${baseUrl}/api/v1/payment/${platform.toLowerCase()}/callback`;
|
||
}
|
||
}
|
||
}
|