Files
bw-mini-app-server/src/migrations/1756900000000-CreatePaymentTables.ts
imeepos 96ae77471c feat: 实现完整的在线支付模块
- 添加支付模块架构,支持微信支付和抖音支付
- 创建支付订单、交易记录、退款记录数据表和实体
- 实现基于适配器模式的多平台支付集成
- 添加统一支付服务,集成会员订阅、积分购买、模板解锁业务
- 完善支付回调处理和退款功能
- 添加完整的DTO验证和API文档
- 集成到主应用模块,完成依赖注入配置
2025-09-25 20:28:30 +08:00

494 lines
12 KiB
TypeScript

import { MigrationInterface, QueryRunner, Table, Index } from 'typeorm';
export class CreatePaymentTables1756900000000 implements MigrationInterface {
name = 'CreatePaymentTables1756900000000';
public async up(queryRunner: QueryRunner): Promise<void> {
// 创建支付订单表
await queryRunner.createTable(
new Table({
name: 'payment_orders',
columns: [
{
name: 'id',
type: 'varchar',
length: '36',
isPrimary: true,
generationStrategy: 'uuid',
},
{
name: 'userId',
type: 'varchar',
length: '36',
},
{
name: 'platform',
type: 'enum',
enum: ['wechat', 'douyin'],
enumName: 'platform_type',
},
{
name: 'orderNo',
type: 'varchar',
length: '64',
isUnique: true,
},
{
name: 'thirdPartyOrderId',
type: 'varchar',
length: '100',
isNullable: true,
},
{
name: 'paymentMethod',
type: 'enum',
enum: ['wechat_miniprogram', 'bytedance_miniprogram'],
},
{
name: 'status',
type: 'enum',
enum: [
'pending',
'paid',
'confirmed',
'cancelled',
'failed',
'refunded',
],
default: "'pending'",
},
{
name: 'businessType',
type: 'enum',
enum: ['subscription', 'credit_purchase', 'template_unlock'],
},
{
name: 'businessId',
type: 'varchar',
length: '100',
},
{
name: 'description',
type: 'varchar',
length: '200',
},
{
name: 'amount',
type: 'bigint',
},
{
name: 'paidAmount',
type: 'bigint',
isNullable: true,
},
{
name: 'currency',
type: 'varchar',
length: '10',
default: "'CNY'",
},
{
name: 'platformUserId',
type: 'varchar',
length: '100',
},
{
name: 'paymentParams',
type: 'json',
isNullable: true,
},
{
name: 'paidAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'expiredAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'notifyUrl',
type: 'varchar',
length: '500',
isNullable: true,
},
{
name: 'metadata',
type: 'json',
isNullable: true,
},
{
name: 'errorMessage',
type: 'text',
isNullable: true,
},
{
name: 'createdAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'updatedAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
onUpdate: 'CURRENT_TIMESTAMP',
},
],
foreignKeys: [
{
columnNames: ['userId'],
referencedColumnNames: ['id'],
referencedTableName: 'users',
onDelete: 'CASCADE',
},
],
}),
true,
);
// 创建支付交易记录表
await queryRunner.createTable(
new Table({
name: 'payment_transactions',
columns: [
{
name: 'id',
type: 'varchar',
length: '36',
isPrimary: true,
generationStrategy: 'uuid',
},
{
name: 'paymentOrderId',
type: 'varchar',
length: '36',
},
{
name: 'thirdPartyTransactionId',
type: 'varchar',
length: '100',
isNullable: true,
},
{
name: 'thirdPartyOrderId',
type: 'varchar',
length: '100',
isNullable: true,
},
{
name: 'transactionType',
type: 'enum',
enum: ['payment', 'refund', 'callback'],
},
{
name: 'status',
type: 'enum',
enum: ['pending', 'success', 'failed'],
default: "'pending'",
},
{
name: 'amount',
type: 'bigint',
},
{
name: 'currency',
type: 'varchar',
length: '10',
default: "'CNY'",
},
{
name: 'transactionTime',
type: 'timestamp',
isNullable: true,
},
{
name: 'description',
type: 'varchar',
length: '200',
isNullable: true,
},
{
name: 'requestData',
type: 'json',
isNullable: true,
},
{
name: 'responseData',
type: 'json',
isNullable: true,
},
{
name: 'callbackData',
type: 'json',
isNullable: true,
},
{
name: 'errorCode',
type: 'varchar',
length: '50',
isNullable: true,
},
{
name: 'errorMessage',
type: 'text',
isNullable: true,
},
{
name: 'processCount',
type: 'int',
default: 1,
},
{
name: 'lastProcessedAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'metadata',
type: 'json',
isNullable: true,
},
{
name: 'createdAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'updatedAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
onUpdate: 'CURRENT_TIMESTAMP',
},
],
foreignKeys: [
{
columnNames: ['paymentOrderId'],
referencedColumnNames: ['id'],
referencedTableName: 'payment_orders',
onDelete: 'CASCADE',
},
],
}),
true,
);
// 创建退款记录表
await queryRunner.createTable(
new Table({
name: 'refund_records',
columns: [
{
name: 'id',
type: 'varchar',
length: '36',
isPrimary: true,
generationStrategy: 'uuid',
},
{
name: 'paymentOrderId',
type: 'varchar',
length: '36',
},
{
name: 'refundNo',
type: 'varchar',
length: '64',
isUnique: true,
},
{
name: 'thirdPartyRefundId',
type: 'varchar',
length: '100',
isNullable: true,
},
{
name: 'thirdPartyTransactionId',
type: 'varchar',
length: '100',
isNullable: true,
},
{
name: 'status',
type: 'enum',
enum: ['pending', 'processing', 'success', 'failed'],
default: "'pending'",
},
{
name: 'refundAmount',
type: 'bigint',
},
{
name: 'originalAmount',
type: 'bigint',
},
{
name: 'currency',
type: 'varchar',
length: '10',
default: "'CNY'",
},
{
name: 'reason',
type: 'varchar',
length: '500',
},
{
name: 'description',
type: 'text',
isNullable: true,
},
{
name: 'requestedBy',
type: 'varchar',
length: '36',
isNullable: true,
},
{
name: 'approvedBy',
type: 'varchar',
length: '36',
isNullable: true,
},
{
name: 'refundedAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'arrivedAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'estimatedArrivalTime',
type: 'timestamp',
isNullable: true,
},
{
name: 'failureReason',
type: 'text',
isNullable: true,
},
{
name: 'errorCode',
type: 'varchar',
length: '50',
isNullable: true,
},
{
name: 'requestData',
type: 'json',
isNullable: true,
},
{
name: 'responseData',
type: 'json',
isNullable: true,
},
{
name: 'callbackData',
type: 'json',
isNullable: true,
},
{
name: 'processCount',
type: 'int',
default: 1,
},
{
name: 'lastProcessedAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'metadata',
type: 'json',
isNullable: true,
},
{
name: 'createdAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'updatedAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
onUpdate: 'CURRENT_TIMESTAMP',
},
],
foreignKeys: [
{
columnNames: ['paymentOrderId'],
referencedColumnNames: ['id'],
referencedTableName: 'payment_orders',
onDelete: 'CASCADE',
},
],
}),
true,
);
// 创建索引
await this.createIndexes(queryRunner);
}
public async down(queryRunner: QueryRunner): Promise<void> {
// 删除表(按依赖关系倒序)
await queryRunner.dropTable('refund_records');
await queryRunner.dropTable('payment_transactions');
await queryRunner.dropTable('payment_orders');
}
private async createIndexes(queryRunner: QueryRunner): Promise<void> {
// payment_orders 表索引
await queryRunner.query(
`CREATE UNIQUE INDEX IDX_PAYMENT_ORDERS_ORDER_NO ON payment_orders (orderNo)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_ORDERS_USER_STATUS ON payment_orders (userId, status)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_ORDERS_PLATFORM_BUSINESS ON payment_orders (platform, businessType)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_ORDERS_CREATED_AT ON payment_orders (createdAt)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_ORDERS_THIRD_PARTY_ID ON payment_orders (thirdPartyOrderId)`,
);
// payment_transactions 表索引
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_TRANSACTIONS_ORDER_ID ON payment_transactions (paymentOrderId)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_TRANSACTIONS_THIRD_PARTY_ID ON payment_transactions (thirdPartyTransactionId)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_TRANSACTIONS_TYPE_STATUS ON payment_transactions (transactionType, status)`,
);
await queryRunner.query(
`CREATE INDEX IDX_PAYMENT_TRANSACTIONS_CREATED_AT ON payment_transactions (createdAt)`,
);
// refund_records 表索引
await queryRunner.query(
`CREATE INDEX IDX_REFUND_RECORDS_ORDER_ID ON refund_records (paymentOrderId)`,
);
await queryRunner.query(
`CREATE UNIQUE INDEX IDX_REFUND_RECORDS_REFUND_NO ON refund_records (refundNo)`,
);
await queryRunner.query(
`CREATE INDEX IDX_REFUND_RECORDS_THIRD_PARTY_ID ON refund_records (thirdPartyRefundId)`,
);
await queryRunner.query(
`CREATE INDEX IDX_REFUND_RECORDS_STATUS ON refund_records (status)`,
);
await queryRunner.query(
`CREATE INDEX IDX_REFUND_RECORDS_CREATED_AT ON refund_records (createdAt)`,
);
}
}