主要更新: 1. 按H5网页端完全重构匹配功能(match页面) - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募 - 资源对接等类型弹出手机号/微信号输入框 - 去掉重新匹配按钮,改为返回按钮 2. 修复所有卡片对齐和宽度问题 - 目录页附录卡片居中 - 首页阅读进度卡片满宽度 - 我的页面菜单卡片对齐 - 推广中心分享卡片统一宽度 3. 修复目录页图标和文字对齐 - section-icon固定40rpx宽高 - section-title与图标垂直居中 4. 更新真实完整文章标题(62篇) - 从book目录读取真实markdown文件名 - 替换之前的简化标题 5. 新增文章数据API - /api/db/chapters - 获取完整书籍结构 - 支持按ID获取单篇文章内容
247 lines
6.7 KiB
TypeScript
247 lines
6.7 KiB
TypeScript
/**
|
||
* 支付网关工厂 (Payment Gateway Factory)
|
||
* 统一管理所有支付网关,实现工厂模式
|
||
*
|
||
* 作者: 卡若
|
||
* 版本: v4.0
|
||
*/
|
||
|
||
import {
|
||
CreateTradeData,
|
||
TradeResult,
|
||
NotifyResult,
|
||
PaymentPlatform,
|
||
PaymentGateway,
|
||
GatewayNotFoundError,
|
||
PaymentMethod
|
||
} from './types';
|
||
|
||
/**
|
||
* 抽象支付网关基类
|
||
*/
|
||
export abstract class AbstractGateway {
|
||
protected config: Record<string, unknown>;
|
||
|
||
constructor(config: Record<string, unknown> = {}) {
|
||
this.config = config;
|
||
}
|
||
|
||
/**
|
||
* 创建交易
|
||
*/
|
||
abstract createTrade(data: CreateTradeData): Promise<TradeResult>;
|
||
|
||
/**
|
||
* 验证签名
|
||
*/
|
||
abstract verifySign(data: Record<string, string>): boolean;
|
||
|
||
/**
|
||
* 解析回调数据
|
||
*/
|
||
abstract parseNotify(data: string | Record<string, string>): NotifyResult;
|
||
|
||
/**
|
||
* 关闭交易
|
||
*/
|
||
abstract closeTrade(tradeSn: string): Promise<boolean>;
|
||
|
||
/**
|
||
* 查询交易
|
||
*/
|
||
abstract queryTrade(tradeSn: string): Promise<NotifyResult | null>;
|
||
|
||
/**
|
||
* 发起退款
|
||
*/
|
||
abstract refund(tradeSn: string, refundSn: string, amount: number, reason?: string): Promise<boolean>;
|
||
|
||
/**
|
||
* 回调成功响应
|
||
*/
|
||
successResponse(): string {
|
||
return 'success';
|
||
}
|
||
|
||
/**
|
||
* 回调失败响应
|
||
*/
|
||
failResponse(): string {
|
||
return 'fail';
|
||
}
|
||
}
|
||
|
||
// 网关类型映射
|
||
type GatewayClass = new (config: Record<string, unknown>) => AbstractGateway;
|
||
|
||
/**
|
||
* 支付网关工厂
|
||
*/
|
||
export class PaymentFactory {
|
||
private static gateways: Map<string, GatewayClass> = new Map();
|
||
|
||
/**
|
||
* 注册支付网关
|
||
*/
|
||
static register(name: string, gatewayClass: GatewayClass): void {
|
||
this.gateways.set(name, gatewayClass);
|
||
console.log(`[PaymentFactory] 注册支付网关: ${name}`);
|
||
}
|
||
|
||
/**
|
||
* 创建支付网关实例
|
||
* @param gateway 网关名称,格式如 'wechat_jsapi',会取下划线前的部分
|
||
*/
|
||
static create(gateway: PaymentGateway | string): AbstractGateway {
|
||
const gatewayName = gateway.split('_')[0] as PaymentPlatform;
|
||
|
||
const GatewayClass = this.gateways.get(gatewayName);
|
||
if (!GatewayClass) {
|
||
throw new GatewayNotFoundError(gateway);
|
||
}
|
||
|
||
const config = this.getGatewayConfig(gatewayName);
|
||
return new GatewayClass(config);
|
||
}
|
||
|
||
/**
|
||
* 获取网关配置
|
||
*/
|
||
private static getGatewayConfig(gateway: PaymentPlatform): Record<string, unknown> {
|
||
const configMap: Record<PaymentPlatform, () => Record<string, unknown>> = {
|
||
alipay: () => ({
|
||
// 支付宝新版接口需要 app_id,如果没有配置则使用 pid(旧版兼容)
|
||
appId: process.env.ALIPAY_APP_ID || process.env.ALIPAY_PID || '2088511801157159',
|
||
pid: process.env.ALIPAY_PID || '2088511801157159',
|
||
sellerEmail: process.env.ALIPAY_SELLER_EMAIL || 'zhengzhiqun@vip.qq.com',
|
||
privateKey: process.env.ALIPAY_PRIVATE_KEY || '',
|
||
publicKey: process.env.ALIPAY_PUBLIC_KEY || '',
|
||
md5Key: process.env.ALIPAY_MD5_KEY || 'lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp',
|
||
enabled: process.env.ALIPAY_ENABLED === 'true',
|
||
mode: process.env.ALIPAY_MODE || 'production',
|
||
}),
|
||
wechat: () => ({
|
||
// 微信支付需要使用绑定了支付功能的服务号AppID
|
||
appId: process.env.WECHAT_APPID || 'wx7c0dbf34ddba300d', // 服务号AppID(已绑定商户号)
|
||
appSecret: process.env.WECHAT_APP_SECRET || 'f865ef18c43dfea6cbe3b1f1aebdb82e',
|
||
serviceAppId: process.env.WECHAT_SERVICE_APPID || 'wx7c0dbf34ddba300d',
|
||
serviceSecret: process.env.WECHAT_SERVICE_SECRET || 'f865ef18c43dfea6cbe3b1f1aebdb82e',
|
||
mchId: process.env.WECHAT_MCH_ID || '1318592501',
|
||
mchKey: process.env.WECHAT_MCH_KEY || 'wx3e31b068be59ddc131b068be59ddc2',
|
||
certPath: process.env.WECHAT_CERT_PATH || '',
|
||
keyPath: process.env.WECHAT_KEY_PATH || '',
|
||
enabled: process.env.WECHAT_ENABLED === 'true',
|
||
mode: process.env.WECHAT_MODE || 'production',
|
||
}),
|
||
paypal: () => ({
|
||
clientId: process.env.PAYPAL_CLIENT_ID || '',
|
||
clientSecret: process.env.PAYPAL_CLIENT_SECRET || '',
|
||
mode: process.env.PAYPAL_MODE || 'sandbox',
|
||
enabled: process.env.PAYPAL_ENABLED === 'true',
|
||
}),
|
||
stripe: () => ({
|
||
publicKey: process.env.STRIPE_PUBLIC_KEY || '',
|
||
secretKey: process.env.STRIPE_SECRET_KEY || '',
|
||
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET || '',
|
||
mode: process.env.STRIPE_MODE || 'test',
|
||
enabled: process.env.STRIPE_ENABLED === 'true',
|
||
}),
|
||
usdt: () => ({
|
||
gatewayType: process.env.USDT_GATEWAY_TYPE || 'nowpayments',
|
||
apiKey: process.env.NOWPAYMENTS_API_KEY || '',
|
||
ipnSecret: process.env.NOWPAYMENTS_IPN_SECRET || '',
|
||
enabled: process.env.USDT_ENABLED === 'true',
|
||
}),
|
||
coin: () => ({
|
||
rate: parseInt(process.env.COIN_RATE || '100', 10),
|
||
enabled: process.env.COIN_ENABLED === 'true',
|
||
}),
|
||
};
|
||
|
||
return configMap[gateway]?.() || {};
|
||
}
|
||
|
||
/**
|
||
* 获取已启用的支付网关列表
|
||
*/
|
||
static getEnabledGateways(): PaymentMethod[] {
|
||
const methods: PaymentMethod[] = [];
|
||
|
||
// 支付宝
|
||
if (process.env.ALIPAY_ENABLED === 'true' || true) { // 默认启用
|
||
methods.push({
|
||
gateway: 'alipay_wap',
|
||
name: '支付宝',
|
||
icon: '/icons/alipay.png',
|
||
enabled: true,
|
||
available: true,
|
||
});
|
||
}
|
||
|
||
// 微信支付
|
||
if (process.env.WECHAT_ENABLED === 'true' || true) { // 默认启用
|
||
methods.push({
|
||
gateway: 'wechat_native',
|
||
name: '微信支付',
|
||
icon: '/icons/wechat.png',
|
||
enabled: true,
|
||
available: true,
|
||
});
|
||
}
|
||
|
||
// PayPal
|
||
if (process.env.PAYPAL_ENABLED === 'true') {
|
||
methods.push({
|
||
gateway: 'paypal',
|
||
name: 'PayPal',
|
||
icon: '/icons/paypal.png',
|
||
enabled: true,
|
||
available: true,
|
||
});
|
||
}
|
||
|
||
// Stripe
|
||
if (process.env.STRIPE_ENABLED === 'true') {
|
||
methods.push({
|
||
gateway: 'stripe',
|
||
name: 'Stripe',
|
||
icon: '/icons/stripe.png',
|
||
enabled: true,
|
||
available: true,
|
||
});
|
||
}
|
||
|
||
// USDT
|
||
if (process.env.USDT_ENABLED === 'true') {
|
||
methods.push({
|
||
gateway: 'usdt',
|
||
name: 'USDT (TRC20)',
|
||
icon: '/icons/usdt.png',
|
||
enabled: true,
|
||
available: true,
|
||
});
|
||
}
|
||
|
||
return methods;
|
||
}
|
||
|
||
/**
|
||
* 检查网关是否已注册
|
||
*/
|
||
static hasGateway(name: string): boolean {
|
||
return this.gateways.has(name);
|
||
}
|
||
|
||
/**
|
||
* 获取所有已注册的网关名称
|
||
*/
|
||
static getRegisteredGateways(): string[] {
|
||
return Array.from(this.gateways.keys());
|
||
}
|
||
}
|
||
|
||
// 导出便捷函数
|
||
export function createPaymentGateway(gateway: PaymentGateway | string): AbstractGateway {
|
||
return PaymentFactory.create(gateway);
|
||
}
|