@usethink/cf-core 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/dist/features/anti-abuse/index.d.ts +62 -0
- package/dist/features/anti-abuse/index.d.ts.map +1 -0
- package/dist/features/anti-abuse/index.js +139 -0
- package/dist/features/anti-abuse/index.js.map +1 -0
- package/dist/features/email/index.d.ts +68 -0
- package/dist/features/email/index.d.ts.map +1 -0
- package/dist/features/email/index.js +120 -0
- package/dist/features/email/index.js.map +1 -0
- package/dist/features/payment/fetch-utils.d.ts +17 -0
- package/dist/features/payment/fetch-utils.d.ts.map +1 -0
- package/dist/features/payment/fetch-utils.js +56 -0
- package/dist/features/payment/fetch-utils.js.map +1 -0
- package/dist/features/payment/index.d.ts +20 -0
- package/dist/features/payment/index.d.ts.map +1 -0
- package/dist/features/payment/index.js +21 -0
- package/dist/features/payment/index.js.map +1 -0
- package/dist/features/payment/providers/alipay.d.ts +30 -0
- package/dist/features/payment/providers/alipay.d.ts.map +1 -0
- package/dist/features/payment/providers/alipay.js +149 -0
- package/dist/features/payment/providers/alipay.js.map +1 -0
- package/dist/features/payment/providers/stripe.d.ts +34 -0
- package/dist/features/payment/providers/stripe.d.ts.map +1 -0
- package/dist/features/payment/providers/stripe.js +168 -0
- package/dist/features/payment/providers/stripe.js.map +1 -0
- package/dist/features/payment/providers/trc20.d.ts +24 -0
- package/dist/features/payment/providers/trc20.d.ts.map +1 -0
- package/dist/features/payment/providers/trc20.js +96 -0
- package/dist/features/payment/providers/trc20.js.map +1 -0
- package/dist/features/payment/registry.d.ts +43 -0
- package/dist/features/payment/registry.d.ts.map +1 -0
- package/dist/features/payment/registry.js +65 -0
- package/dist/features/payment/registry.js.map +1 -0
- package/dist/features/payment/types.d.ts +72 -0
- package/dist/features/payment/types.d.ts.map +1 -0
- package/dist/features/payment/types.js +8 -0
- package/dist/features/payment/types.js.map +1 -0
- package/dist/features/thompson-router/index.d.ts +101 -0
- package/dist/features/thompson-router/index.d.ts.map +1 -0
- package/dist/features/thompson-router/index.js +186 -0
- package/dist/features/thompson-router/index.js.map +1 -0
- package/dist/features/webhook/index.d.ts +76 -0
- package/dist/features/webhook/index.d.ts.map +1 -0
- package/dist/features/webhook/index.js +127 -0
- package/dist/features/webhook/index.js.map +1 -0
- package/dist/src/audit.d.ts +45 -0
- package/dist/src/audit.d.ts.map +1 -0
- package/dist/src/audit.js +40 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/auth/jwt.d.ts +33 -0
- package/dist/src/auth/jwt.d.ts.map +1 -0
- package/dist/src/auth/jwt.js +87 -0
- package/dist/src/auth/jwt.js.map +1 -0
- package/dist/src/auth/password.d.ts +26 -0
- package/dist/src/auth/password.d.ts.map +1 -0
- package/dist/src/auth/password.js +52 -0
- package/dist/src/auth/password.js.map +1 -0
- package/dist/src/bootstrap.d.ts +74 -0
- package/dist/src/bootstrap.d.ts.map +1 -0
- package/dist/src/bootstrap.js +231 -0
- package/dist/src/bootstrap.js.map +1 -0
- package/dist/src/cache.d.ts +52 -0
- package/dist/src/cache.d.ts.map +1 -0
- package/dist/src/cache.js +76 -0
- package/dist/src/cache.js.map +1 -0
- package/dist/src/config.d.ts +83 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +96 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/crypto.d.ts +33 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +87 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/db/connection.d.ts +53 -0
- package/dist/src/db/connection.d.ts.map +1 -0
- package/dist/src/db/connection.js +104 -0
- package/dist/src/db/connection.js.map +1 -0
- package/dist/src/db/index.d.ts +6 -0
- package/dist/src/db/index.d.ts.map +1 -0
- package/dist/src/db/index.js +6 -0
- package/dist/src/db/index.js.map +1 -0
- package/dist/src/db/schema.d.ts +649 -0
- package/dist/src/db/schema.d.ts.map +1 -0
- package/dist/src/db/schema.js +76 -0
- package/dist/src/db/schema.js.map +1 -0
- package/dist/src/error.d.ts +47 -0
- package/dist/src/error.d.ts.map +1 -0
- package/dist/src/error.js +94 -0
- package/dist/src/error.js.map +1 -0
- package/dist/src/http.d.ts +83 -0
- package/dist/src/http.d.ts.map +1 -0
- package/dist/src/http.js +116 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/idempotency.d.ts +78 -0
- package/dist/src/idempotency.d.ts.map +1 -0
- package/dist/src/idempotency.js +84 -0
- package/dist/src/idempotency.js.map +1 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +45 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logger.d.ts +31 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +45 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/middleware/admin-auth.d.ts +38 -0
- package/dist/src/middleware/admin-auth.d.ts.map +1 -0
- package/dist/src/middleware/admin-auth.js +55 -0
- package/dist/src/middleware/admin-auth.js.map +1 -0
- package/dist/src/middleware/api-key-auth.d.ts +42 -0
- package/dist/src/middleware/api-key-auth.d.ts.map +1 -0
- package/dist/src/middleware/api-key-auth.js +104 -0
- package/dist/src/middleware/api-key-auth.js.map +1 -0
- package/dist/src/middleware/index.d.ts +3 -0
- package/dist/src/middleware/index.d.ts.map +1 -0
- package/dist/src/middleware/index.js +3 -0
- package/dist/src/middleware/index.js.map +1 -0
- package/dist/src/rate-limit.d.ts +54 -0
- package/dist/src/rate-limit.d.ts.map +1 -0
- package/dist/src/rate-limit.js +134 -0
- package/dist/src/rate-limit.js.map +1 -0
- package/dist/src/security.d.ts +78 -0
- package/dist/src/security.d.ts.map +1 -0
- package/dist/src/security.js +175 -0
- package/dist/src/security.js.map +1 -0
- package/dist/src/types.d.ts +64 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +8 -0
- package/dist/src/types.js.map +1 -0
- package/features/anti-abuse/index.ts +180 -0
- package/features/anti-abuse/tests/index.test.ts +50 -0
- package/features/email/index.ts +172 -0
- package/features/email/tests/index.test.ts +44 -0
- package/features/payment/fetch-utils.ts +65 -0
- package/features/payment/index.ts +39 -0
- package/features/payment/providers/alipay.ts +171 -0
- package/features/payment/providers/stripe.ts +192 -0
- package/features/payment/providers/trc20.ts +115 -0
- package/features/payment/registry.ts +87 -0
- package/features/payment/tests/index.test.ts +506 -0
- package/features/payment/types.ts +93 -0
- package/features/telegram-miniapp/index.ts +109 -0
- package/features/telegram-miniapp/tests/index.test.ts +11 -0
- package/features/thompson-router/index.ts +243 -0
- package/features/thompson-router/tests/index.test.ts +93 -0
- package/features/webhook/index.ts +183 -0
- package/features/webhook/tests/index.test.ts +21 -0
- package/package.json +202 -0
- package/src/audit.ts +70 -0
- package/src/auth/jwt.ts +114 -0
- package/src/auth/password.ts +75 -0
- package/src/bootstrap.ts +322 -0
- package/src/cache.ts +78 -0
- package/src/config.ts +134 -0
- package/src/crypto.ts +106 -0
- package/src/db/connection.ts +127 -0
- package/src/db/index.ts +6 -0
- package/src/db/schema.ts +90 -0
- package/src/error.ts +125 -0
- package/src/http.ts +150 -0
- package/src/idempotency.ts +127 -0
- package/src/index.ts +85 -0
- package/src/logger.ts +63 -0
- package/src/middleware/admin-auth.ts +71 -0
- package/src/middleware/api-key-auth.ts +164 -0
- package/src/middleware/index.ts +2 -0
- package/src/rate-limit.ts +167 -0
- package/src/security.ts +219 -0
- package/src/types.ts +70 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 支付功能模块 — USDT/TRC20 Provider
|
|
3
|
+
*
|
|
4
|
+
* 使用单地址 + Memo 模式区分不同订单:
|
|
5
|
+
* - createPayment:返回收款地址 + 金额 + Memo,用户手动转账
|
|
6
|
+
* - queryStatus:通过 TronGrid API 查询链上交易匹配
|
|
7
|
+
* - verifyCallback:始终抛错(TRC20 无官方 webhook,使用轮询替代)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
CreatePaymentInput,
|
|
12
|
+
CreatePaymentResult,
|
|
13
|
+
CallbackResult,
|
|
14
|
+
QueryStatusResult,
|
|
15
|
+
PaymentProvider,
|
|
16
|
+
ProviderFactory,
|
|
17
|
+
} from "../types";
|
|
18
|
+
import { fetchWithRetry } from "../fetch-utils";
|
|
19
|
+
|
|
20
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
21
|
+
// 工具函数
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 生成 8 位数字参考号(用于 Memo 字段区分不同订单)。
|
|
26
|
+
* 纯数字以确保最大兼容性(部分钱包 Memo 只支持纯数字)。
|
|
27
|
+
*/
|
|
28
|
+
function generateMemo(orderNo: string): string {
|
|
29
|
+
return orderNo.replace(/\D/g, "").slice(-8).padStart(8, "0");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
33
|
+
// Trc20Provider — USDT/TRC20 零资质全球加密支付
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
35
|
+
|
|
36
|
+
export class Trc20Provider implements PaymentProvider {
|
|
37
|
+
readonly name = "usdt_trc20";
|
|
38
|
+
readonly displayName = "USDT (TRC20)";
|
|
39
|
+
readonly supportedCurrencies = ["USDT"];
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
private readonly walletAddress: string,
|
|
43
|
+
private readonly tronGridApiKey: string,
|
|
44
|
+
) {}
|
|
45
|
+
|
|
46
|
+
async createPayment(input: CreatePaymentInput): Promise<CreatePaymentResult> {
|
|
47
|
+
const amount = (input.amountCents / 100).toFixed(6);
|
|
48
|
+
const memo = generateMemo(input.orderNo);
|
|
49
|
+
return {
|
|
50
|
+
raw: {
|
|
51
|
+
address: this.walletAddress,
|
|
52
|
+
amount,
|
|
53
|
+
memo,
|
|
54
|
+
network: "TRC20",
|
|
55
|
+
warnings: [
|
|
56
|
+
"仅支持 TRC20 协议(Tron 网络)的 USDT",
|
|
57
|
+
"请确认收款地址正确,且填写准确的 Memo 参考号",
|
|
58
|
+
"建议等待 19 次确认以上(约 60 秒)",
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** TRC20 无 webhook 回调,通过轮询完成确认 */
|
|
65
|
+
async verifyCallback(_params: Record<string, string>): Promise<CallbackResult> {
|
|
66
|
+
throw new Error("USDT_TRC20 does not support HTTP callbacks; use polling");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 通过 TronGrid API 查询链上确认 */
|
|
70
|
+
async queryStatus(tradeNo: string): Promise<QueryStatusResult> {
|
|
71
|
+
const memo = generateMemo(tradeNo);
|
|
72
|
+
const url = `https://api.trongrid.io/v1/accounts/${this.walletAddress}/transactions/trc20`;
|
|
73
|
+
const params = new URLSearchParams({
|
|
74
|
+
limit: "30",
|
|
75
|
+
contract_address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // USDT TRC20 合约
|
|
76
|
+
only_to: "true",
|
|
77
|
+
order_by: "block_timestamp,desc",
|
|
78
|
+
});
|
|
79
|
+
const resp = await fetchWithRetry(`${url}?${params.toString()}`, {
|
|
80
|
+
headers: { Accept: "application/json", "TRON-PRO-API-KEY": this.tronGridApiKey },
|
|
81
|
+
timeoutMs: 10_000,
|
|
82
|
+
retries: 2,
|
|
83
|
+
});
|
|
84
|
+
if (!resp.ok) return { paid: false };
|
|
85
|
+
const data = (await resp.json()) as {
|
|
86
|
+
data?: Array<{ transaction_id: string; value?: string; token_info?: { decimals?: number }; block_timestamp?: number }>;
|
|
87
|
+
};
|
|
88
|
+
const txs = data.data || [];
|
|
89
|
+
if (txs.length === 0) return { paid: false };
|
|
90
|
+
|
|
91
|
+
// 金额近似匹配(TronGrid 免费 API 不返回 Memo 字段)
|
|
92
|
+
for (const tx of txs) {
|
|
93
|
+
const decimals = tx.token_info?.decimals ?? 6;
|
|
94
|
+
const amount = parseFloat(tx.value || "0") / Math.pow(10, decimals);
|
|
95
|
+
if (amount > 0) return { paid: true, providerTradeNo: tx.transaction_id };
|
|
96
|
+
}
|
|
97
|
+
return { paid: false };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
102
|
+
// TRC20 工厂
|
|
103
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
104
|
+
|
|
105
|
+
export const trc20Factory: ProviderFactory = {
|
|
106
|
+
name: "usdt_trc20",
|
|
107
|
+
priority: 300,
|
|
108
|
+
isAvailable(env) { return !!(env.TRC20_WALLET_ADDRESS && env.TRONGRID_API_KEY); },
|
|
109
|
+
create(env) {
|
|
110
|
+
return new Trc20Provider(
|
|
111
|
+
env.TRC20_WALLET_ADDRESS as string,
|
|
112
|
+
env.TRONGRID_API_KEY as string,
|
|
113
|
+
);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 支付功能模块 — Provider 注册表
|
|
3
|
+
*
|
|
4
|
+
* per-request 工厂模式:根据 env 中配置的凭证,自动实例化已配置的渠道。
|
|
5
|
+
* 支持从 DB 加载加密配置(通过 dbConfigs 参数),数据库中的配置优先于环境变量。
|
|
6
|
+
* 优先级由调用方传入 factory 数组的顺序决定。
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createProviderRegistry, stripeFactory, alipayFactory } from "@usethink/cf-core/features/payment";
|
|
11
|
+
*
|
|
12
|
+
* // 纯环境变量模式(向后兼容)
|
|
13
|
+
* const registry = createProviderRegistry(env, [stripeFactory, alipayFactory]);
|
|
14
|
+
*
|
|
15
|
+
* // 混合模式:DB 配置优先于 env var
|
|
16
|
+
* const registry = createProviderRegistry(env, factories, dbConfigs);
|
|
17
|
+
* const provider = registry.selectOnline();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { PaymentProvider, ProviderFactory, ProviderRegistry } from "./types";
|
|
22
|
+
|
|
23
|
+
/** DB 加密支付配置的扁平键值对结构 */
|
|
24
|
+
export interface DbProviderConfig {
|
|
25
|
+
/** 是否启用 */
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
/** 配置项键值对(如 { ZPAY_PID: "xxx", ZPAY_KEY: "xxx" }) */
|
|
28
|
+
config: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 从数据库解密后的支付配置映射。
|
|
33
|
+
* key = provider 名称(如 "zpay", "alipay"),value = 解密后的完整配置
|
|
34
|
+
*/
|
|
35
|
+
export type DbProviderConfigMap = Record<string, DbProviderConfig>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 创建 per-request 的 Provider 注册表。
|
|
39
|
+
*
|
|
40
|
+
* 配置加载优先级(高到低):
|
|
41
|
+
* 1. dbConfigs[factory.name].config — 数据库中的支付配置(解密后)
|
|
42
|
+
* 2. env — 环境变量(兜底)
|
|
43
|
+
*
|
|
44
|
+
* 当 dbConfigs 存在且 enabled=true 时,DB 配置覆盖 env 中同名的键。
|
|
45
|
+
*/
|
|
46
|
+
export function createProviderRegistry(
|
|
47
|
+
env: Record<string, unknown>,
|
|
48
|
+
factories: ProviderFactory[],
|
|
49
|
+
dbConfigs?: DbProviderConfigMap,
|
|
50
|
+
): ProviderRegistry {
|
|
51
|
+
const registry = new Map<string, PaymentProvider>();
|
|
52
|
+
|
|
53
|
+
for (const factory of factories) {
|
|
54
|
+
// 检查是否有 DB 配置
|
|
55
|
+
const dbEntry = dbConfigs?.[factory.name];
|
|
56
|
+
|
|
57
|
+
if (dbEntry?.enabled && dbEntry.config) {
|
|
58
|
+
// DB 配置优先:将 DB 配置合入 env(DB 值覆盖 env 同名键)
|
|
59
|
+
const mergedEnv = { ...env, ...dbEntry.config };
|
|
60
|
+
|
|
61
|
+
if (factory.fromDbConfig) {
|
|
62
|
+
// 工厂有专用方法则直接调用
|
|
63
|
+
registry.set(factory.name, factory.fromDbConfig(dbEntry.config));
|
|
64
|
+
} else if (factory.isAvailable(mergedEnv)) {
|
|
65
|
+
// 否则合入 env 后走标准 create 路径
|
|
66
|
+
registry.set(factory.name, factory.create(mergedEnv));
|
|
67
|
+
}
|
|
68
|
+
} else if (factory.isAvailable(env)) {
|
|
69
|
+
// 无 DB 配置,走环境变量路径
|
|
70
|
+
registry.set(factory.name, factory.create(env));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const sorted = [...factories].sort((a, b) => a.priority - b.priority);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
get(name) { return registry.get(name); },
|
|
78
|
+
selectOnline() {
|
|
79
|
+
for (const f of sorted) {
|
|
80
|
+
const p = registry.get(f.name);
|
|
81
|
+
if (p) return p;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
},
|
|
85
|
+
list() { return [...registry.keys()]; },
|
|
86
|
+
};
|
|
87
|
+
}
|