afdian-kit 0.1.0 → 0.1.1
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 +13 -1
- package/dist/index.d.ts +31 -3
- package/dist/index.js +89 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,10 +37,13 @@ export default defineConfig({
|
|
|
37
37
|
```ts
|
|
38
38
|
import {
|
|
39
39
|
price, listPlans, listProducts, getProduct,
|
|
40
|
-
|
|
40
|
+
priceSync, listPlansSync, listProductsSync, getProductSync,
|
|
41
|
+
verify, resolveOrder, buildUrl, resolveOrderSync, buildUrlSync,
|
|
41
42
|
configure, defineConfig,
|
|
42
43
|
} from "afdian-kit";
|
|
43
44
|
|
|
45
|
+
// ── 异步版(Node 场景:首次调用自动发现并加载 afdian.config)──
|
|
46
|
+
|
|
44
47
|
// 价格(金额单位:分)
|
|
45
48
|
await price("membership", { months: 6 });
|
|
46
49
|
// → { kind:"plan", amount:6768, original:7200, rate:0.06, months:6, name:"忍3作战室会员", ... }
|
|
@@ -55,6 +58,15 @@ await listPlans({ from: 6, to: 12 }); // 只要 6、12 两档
|
|
|
55
58
|
await listProducts(); // [{ key, plan_id, name, price }]
|
|
56
59
|
await getProduct("starter_pack"); // 未配置 → null
|
|
57
60
|
|
|
61
|
+
// ── 同步版 *Sync(打包器/Workers 场景:入口 configure(config) 后即可同步调用)──
|
|
62
|
+
// 调用方只给 key/months —— 月单价、折扣、plan_id 全部由 config 绑定,无需复述
|
|
63
|
+
priceSync("membership", { months: 6 }); // React render 里直接用,无 loading 态
|
|
64
|
+
listPlansSync(); // → 与 listPlans() 同结果
|
|
65
|
+
listProductsSync(); getProductSync("starter_pack");
|
|
66
|
+
buildUrlSync({ entry: "membership", userId, months: 6 });
|
|
67
|
+
resolveOrderSync(order);
|
|
68
|
+
// 未 configure(且从未 await 过异步版)时调用 *Sync → 抛「配置尚未加载」
|
|
69
|
+
|
|
58
70
|
// 验签(async —— Web Crypto 仅异步 API;不依赖 config)
|
|
59
71
|
await verify(rawBody); // → { valid:true, type, order } | { valid:false, reason }
|
|
60
72
|
await verify(rawBody, { publicKey }); // 显式覆盖公钥(轮换/测试)
|
package/dist/index.d.ts
CHANGED
|
@@ -129,6 +129,13 @@ export declare function defineConfig(config: AfdianConfig): AfdianConfig;
|
|
|
129
129
|
export declare function configure(config: AfdianConfig): void;
|
|
130
130
|
/** 重置缓存(测试用) */
|
|
131
131
|
export declare function resetConfig(): void;
|
|
132
|
+
/** 获取配置(内部统一入口):已配置直接返回,否则自动发现 */
|
|
133
|
+
export declare function getConfig(): Promise<AfdianConfig>;
|
|
134
|
+
/**
|
|
135
|
+
* 同步获取配置(Sync 系列 API 使用)
|
|
136
|
+
* 需已 configure(config)(打包器/Workers 入口)或此前任一异步方法完成过自动发现(Node)
|
|
137
|
+
*/
|
|
138
|
+
export declare function getConfigSync(): AfdianConfig;
|
|
132
139
|
//#endregion
|
|
133
140
|
//#region src/pricing.d.ts
|
|
134
141
|
/** 官方可购档位(一次性购买仅此六档,钉死) */
|
|
@@ -136,7 +143,7 @@ export declare const MONTH_TIERS: readonly [1, 3, 6, 12, 24, 36];
|
|
|
136
143
|
/** 官方折扣表(钉死) */
|
|
137
144
|
export declare const DISCOUNTS: Readonly<Record<number, number>>;
|
|
138
145
|
/**
|
|
139
|
-
*
|
|
146
|
+
* 订阅方案价格计算(纯函数,底层)
|
|
140
147
|
* 金额 = Math.round(monthly × months × (1 − rate)),单位分
|
|
141
148
|
*/
|
|
142
149
|
export declare function calcPlanPrice({ monthly, months, discount }: {
|
|
@@ -148,7 +155,7 @@ export declare function calcPlanPrice({ monthly, months, discount }: {
|
|
|
148
155
|
original: number;
|
|
149
156
|
rate: number;
|
|
150
157
|
};
|
|
151
|
-
/**
|
|
158
|
+
/** 计算某方案全部档位价格(纯函数,底层) */
|
|
152
159
|
export declare function calcTiers({ monthly, discount }: {
|
|
153
160
|
monthly: number;
|
|
154
161
|
discount: boolean;
|
|
@@ -158,7 +165,7 @@ export declare function calcTiers({ monthly, discount }: {
|
|
|
158
165
|
rate: number;
|
|
159
166
|
months: 1 | 3 | 6 | 12 | 24 | 36;
|
|
160
167
|
}[];
|
|
161
|
-
/**
|
|
168
|
+
/** 订阅方案/商品价格(key 未配置则抛错;product 忽略 months) */
|
|
162
169
|
export declare function price(key: string, opts?: {
|
|
163
170
|
months?: number;
|
|
164
171
|
}): Promise<PriceResult>;
|
|
@@ -171,6 +178,19 @@ export declare function listPlans(opts?: {
|
|
|
171
178
|
export declare function listProducts(): Promise<ProductInfo[]>;
|
|
172
179
|
/** 单个固定商品(未配置返回 null) */
|
|
173
180
|
export declare function getProduct(key: string): Promise<ProductInfo | null>;
|
|
181
|
+
/** 同步版 price:需已 configure(config),否则抛错 */
|
|
182
|
+
export declare function priceSync(key: string, opts?: {
|
|
183
|
+
months?: number;
|
|
184
|
+
}): PriceResult;
|
|
185
|
+
/** 同步版 listPlans:需已 configure(config),否则抛错 */
|
|
186
|
+
export declare function listPlansSync(opts?: {
|
|
187
|
+
from?: number;
|
|
188
|
+
to?: number;
|
|
189
|
+
}): PlanInfo[];
|
|
190
|
+
/** 同步版 listProducts:需已 configure(config),否则抛错 */
|
|
191
|
+
export declare function listProductsSync(): ProductInfo[];
|
|
192
|
+
/** 同步版 getProduct:需已 configure(config),否则抛错 */
|
|
193
|
+
export declare function getProductSync(key: string): ProductInfo | null;
|
|
174
194
|
//#endregion
|
|
175
195
|
//#region src/verify.d.ts
|
|
176
196
|
/**
|
|
@@ -211,6 +231,14 @@ export declare function buildUrl(opts: {
|
|
|
211
231
|
userId: string;
|
|
212
232
|
months?: number;
|
|
213
233
|
}): Promise<string>;
|
|
234
|
+
/** 同步版 resolveOrder */
|
|
235
|
+
export declare function resolveOrderSync(order: Pick<AfdianOrder, "plan_id" | "month">): OrderResolution | null;
|
|
236
|
+
/** 同步版 buildUrl */
|
|
237
|
+
export declare function buildUrlSync(opts: {
|
|
238
|
+
entry: string;
|
|
239
|
+
userId: string;
|
|
240
|
+
months?: number;
|
|
241
|
+
}): string;
|
|
214
242
|
//#endregion
|
|
215
243
|
//#region src/handler.d.ts
|
|
216
244
|
/**
|
package/dist/index.js
CHANGED
|
@@ -49,6 +49,14 @@ async function getConfig() {
|
|
|
49
49
|
});
|
|
50
50
|
return loading;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* 同步获取配置(Sync 系列 API 使用)
|
|
54
|
+
* 需已 configure(config)(打包器/Workers 入口)或此前任一异步方法完成过自动发现(Node)
|
|
55
|
+
*/
|
|
56
|
+
function getConfigSync() {
|
|
57
|
+
if (cached) return cached;
|
|
58
|
+
throw new Error("配置尚未加载:请先在入口调用 configure(config);Node 下也可先 await 任一异步方法(如 price/listPlans)触发自动发现");
|
|
59
|
+
}
|
|
52
60
|
async function discover() {
|
|
53
61
|
if (!isNode()) throw new Error("自动发现 afdian.config 仅支持 Node 环境;Workers/打包器环境请先调用 configure(config)");
|
|
54
62
|
const fs = await importNodeBuiltin("fs");
|
|
@@ -92,7 +100,7 @@ const DISCOUNTS = {
|
|
|
92
100
|
};
|
|
93
101
|
const TIER_HINT = "1/3/6/12/24/36";
|
|
94
102
|
/**
|
|
95
|
-
*
|
|
103
|
+
* 订阅方案价格计算(纯函数,底层)
|
|
96
104
|
* 金额 = Math.round(monthly × months × (1 − rate)),单位分
|
|
97
105
|
*/
|
|
98
106
|
function calcPlanPrice({ monthly, months, discount }) {
|
|
@@ -106,7 +114,7 @@ function calcPlanPrice({ monthly, months, discount }) {
|
|
|
106
114
|
rate
|
|
107
115
|
};
|
|
108
116
|
}
|
|
109
|
-
/**
|
|
117
|
+
/** 计算某方案全部档位价格(纯函数,底层) */
|
|
110
118
|
function calcTiers({ monthly, discount }) {
|
|
111
119
|
return MONTH_TIERS.map((months) => ({
|
|
112
120
|
months,
|
|
@@ -117,22 +125,21 @@ function calcTiers({ monthly, discount }) {
|
|
|
117
125
|
})
|
|
118
126
|
}));
|
|
119
127
|
}
|
|
120
|
-
|
|
121
|
-
async function price(key, opts) {
|
|
122
|
-
const cfg = await getConfig();
|
|
128
|
+
function priceImpl(cfg, key, opts) {
|
|
123
129
|
const plan = cfg.plans[key];
|
|
124
130
|
if (plan) {
|
|
131
|
+
const entry = plan;
|
|
125
132
|
const months = opts?.months ?? 1;
|
|
126
133
|
const { amount, original, rate } = calcPlanPrice({
|
|
127
|
-
monthly:
|
|
134
|
+
monthly: entry.monthly,
|
|
128
135
|
months,
|
|
129
|
-
discount:
|
|
136
|
+
discount: entry.discount
|
|
130
137
|
});
|
|
131
138
|
return {
|
|
132
139
|
key,
|
|
133
140
|
kind: "plan",
|
|
134
|
-
name:
|
|
135
|
-
plan_id:
|
|
141
|
+
name: entry.name,
|
|
142
|
+
plan_id: entry.plan_id,
|
|
136
143
|
months,
|
|
137
144
|
amount,
|
|
138
145
|
original,
|
|
@@ -140,19 +147,20 @@ async function price(key, opts) {
|
|
|
140
147
|
};
|
|
141
148
|
}
|
|
142
149
|
const product = cfg.products[key];
|
|
143
|
-
if (product)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
if (product) {
|
|
151
|
+
const entry = product;
|
|
152
|
+
return {
|
|
153
|
+
key,
|
|
154
|
+
kind: "product",
|
|
155
|
+
name: entry.name,
|
|
156
|
+
plan_id: entry.plan_id,
|
|
157
|
+
amount: entry.price,
|
|
158
|
+
fixed: true
|
|
159
|
+
};
|
|
160
|
+
}
|
|
151
161
|
throw new Error(`未找到配置项: ${key}(检查 afdian.config 的 plans/products)`);
|
|
152
162
|
}
|
|
153
|
-
|
|
154
|
-
async function listPlans(opts) {
|
|
155
|
-
const cfg = await getConfig();
|
|
163
|
+
function listPlansImpl(cfg, opts) {
|
|
156
164
|
return Object.entries(cfg.plans).map(([key, plan]) => ({
|
|
157
165
|
key,
|
|
158
166
|
plan_id: plan.plan_id,
|
|
@@ -162,9 +170,7 @@ async function listPlans(opts) {
|
|
|
162
170
|
tiers: calcTiers(plan).filter((t) => (opts?.from === void 0 || t.months >= opts.from) && (opts?.to === void 0 || t.months <= opts.to))
|
|
163
171
|
}));
|
|
164
172
|
}
|
|
165
|
-
|
|
166
|
-
async function listProducts() {
|
|
167
|
-
const cfg = await getConfig();
|
|
173
|
+
function listProductsImpl(cfg) {
|
|
168
174
|
return Object.entries(cfg.products).map(([key, product]) => ({
|
|
169
175
|
key,
|
|
170
176
|
plan_id: product.plan_id,
|
|
@@ -172,9 +178,8 @@ async function listProducts() {
|
|
|
172
178
|
price: product.price
|
|
173
179
|
}));
|
|
174
180
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const product = (await getConfig()).products[key];
|
|
181
|
+
function getProductImpl(cfg, key) {
|
|
182
|
+
const product = cfg.products[key];
|
|
178
183
|
if (!product) return null;
|
|
179
184
|
return {
|
|
180
185
|
key,
|
|
@@ -183,6 +188,38 @@ async function getProduct(key) {
|
|
|
183
188
|
price: product.price
|
|
184
189
|
};
|
|
185
190
|
}
|
|
191
|
+
/** 订阅方案/商品价格(key 未配置则抛错;product 忽略 months) */
|
|
192
|
+
async function price(key, opts) {
|
|
193
|
+
return priceImpl(await getConfig(), key, opts);
|
|
194
|
+
}
|
|
195
|
+
/** 订阅方案列表(含逐档价格表,可按档位区间过滤) */
|
|
196
|
+
async function listPlans(opts) {
|
|
197
|
+
return listPlansImpl(await getConfig(), opts);
|
|
198
|
+
}
|
|
199
|
+
/** 固定商品列表 */
|
|
200
|
+
async function listProducts() {
|
|
201
|
+
return listProductsImpl(await getConfig());
|
|
202
|
+
}
|
|
203
|
+
/** 单个固定商品(未配置返回 null) */
|
|
204
|
+
async function getProduct(key) {
|
|
205
|
+
return getProductImpl(await getConfig(), key);
|
|
206
|
+
}
|
|
207
|
+
/** 同步版 price:需已 configure(config),否则抛错 */
|
|
208
|
+
function priceSync(key, opts) {
|
|
209
|
+
return priceImpl(getConfigSync(), key, opts);
|
|
210
|
+
}
|
|
211
|
+
/** 同步版 listPlans:需已 configure(config),否则抛错 */
|
|
212
|
+
function listPlansSync(opts) {
|
|
213
|
+
return listPlansImpl(getConfigSync(), opts);
|
|
214
|
+
}
|
|
215
|
+
/** 同步版 listProducts:需已 configure(config),否则抛错 */
|
|
216
|
+
function listProductsSync() {
|
|
217
|
+
return listProductsImpl(getConfigSync());
|
|
218
|
+
}
|
|
219
|
+
/** 同步版 getProduct:需已 configure(config),否则抛错 */
|
|
220
|
+
function getProductSync(key) {
|
|
221
|
+
return getProductImpl(getConfigSync(), key);
|
|
222
|
+
}
|
|
186
223
|
//#endregion
|
|
187
224
|
//#region src/verify.ts
|
|
188
225
|
/**
|
|
@@ -273,12 +310,7 @@ async function verify(payload, opts) {
|
|
|
273
310
|
}
|
|
274
311
|
//#endregion
|
|
275
312
|
//#region src/order.ts
|
|
276
|
-
|
|
277
|
-
* 按 order.plan_id 匹配配置并给出该单应收金额(应用做金额核对的辅助)
|
|
278
|
-
* 匹配不到返回 null(应用记 rejected)
|
|
279
|
-
*/
|
|
280
|
-
async function resolveOrder(order) {
|
|
281
|
-
const cfg = await getConfig();
|
|
313
|
+
function resolveOrderImpl(cfg, order) {
|
|
282
314
|
const planHit = Object.entries(cfg.plans).find(([, entry]) => entry.plan_id === order.plan_id);
|
|
283
315
|
if (planHit) {
|
|
284
316
|
const [key, entry] = planHit;
|
|
@@ -311,13 +343,7 @@ async function resolveOrder(order) {
|
|
|
311
343
|
}
|
|
312
344
|
return null;
|
|
313
345
|
}
|
|
314
|
-
|
|
315
|
-
* 生成爱发电下单跳转 URL
|
|
316
|
-
* - plans entry:product_type=0,months 默认 1(非法档位抛错)
|
|
317
|
-
* - products entry:product_type=1,不带 month
|
|
318
|
-
*/
|
|
319
|
-
async function buildUrl(opts) {
|
|
320
|
-
const cfg = await getConfig();
|
|
346
|
+
function buildUrlImpl(cfg, opts) {
|
|
321
347
|
const plan = cfg.plans[opts.entry];
|
|
322
348
|
if (plan) {
|
|
323
349
|
const months = opts.months ?? 1;
|
|
@@ -341,6 +367,29 @@ async function buildUrl(opts) {
|
|
|
341
367
|
})}`;
|
|
342
368
|
throw new Error(`未找到配置项: ${opts.entry}`);
|
|
343
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* 按 order.plan_id 匹配配置并给出该单应收金额(应用做金额核对的辅助)
|
|
372
|
+
* 匹配不到返回 null(应用记 rejected)
|
|
373
|
+
*/
|
|
374
|
+
async function resolveOrder(order) {
|
|
375
|
+
return resolveOrderImpl(await getConfig(), order);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* 生成爱发电下单跳转 URL
|
|
379
|
+
* - plans entry:product_type=0,months 默认 1(非法档位抛错)
|
|
380
|
+
* - products entry:product_type=1,不带 month
|
|
381
|
+
*/
|
|
382
|
+
async function buildUrl(opts) {
|
|
383
|
+
return buildUrlImpl(await getConfig(), opts);
|
|
384
|
+
}
|
|
385
|
+
/** 同步版 resolveOrder */
|
|
386
|
+
function resolveOrderSync(order) {
|
|
387
|
+
return resolveOrderImpl(getConfigSync(), order);
|
|
388
|
+
}
|
|
389
|
+
/** 同步版 buildUrl */
|
|
390
|
+
function buildUrlSync(opts) {
|
|
391
|
+
return buildUrlImpl(getConfigSync(), opts);
|
|
392
|
+
}
|
|
344
393
|
//#endregion
|
|
345
394
|
//#region src/handler.ts
|
|
346
395
|
/**
|
|
@@ -421,4 +470,4 @@ function createWebhookHandler(deps) {
|
|
|
421
470
|
};
|
|
422
471
|
}
|
|
423
472
|
//#endregion
|
|
424
|
-
export { AFDIAN_PUBLIC_KEY, DISCOUNTS, MONTH_TIERS, buildSignStr, buildUrl, calcPlanPrice, calcTiers, configure, createWebhookHandler, defineConfig, getProduct, listPlans, listProducts, price, resetConfig, resolveOrder, verify };
|
|
473
|
+
export { AFDIAN_PUBLIC_KEY, DISCOUNTS, MONTH_TIERS, buildSignStr, buildUrl, buildUrlSync, calcPlanPrice, calcTiers, configure, createWebhookHandler, defineConfig, getConfig, getConfigSync, getProduct, getProductSync, listPlans, listPlansSync, listProducts, listProductsSync, price, priceSync, resetConfig, resolveOrder, resolveOrderSync, verify };
|