@windrun-huaiin/backend-core 14.0.0 → 14.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.
@@ -23,6 +23,9 @@ function createErrorResponse(message, status = 400) {
23
23
  }
24
24
  const SOURCE_REF_MAX_LENGTH = 2048;
25
25
  const QUERY_PARAM_MAX_LENGTH = 512;
26
+ const USER_AGENT_MAX_LENGTH = 1024;
27
+ const FIRST_TOUCH_HEADER_MAX_LENGTH = 4096;
28
+ const FIRST_TOUCH_HEADER_NAME = 'x-first-touch';
26
29
  function normalizeSourceRef(ref) {
27
30
  if (!ref) {
28
31
  return null;
@@ -47,6 +50,28 @@ function normalizeQueryParam(value) {
47
50
  ? trimmed.slice(0, QUERY_PARAM_MAX_LENGTH)
48
51
  : trimmed;
49
52
  }
53
+ function decodeHeaderValue(value) {
54
+ try {
55
+ return decodeURIComponent(value);
56
+ }
57
+ catch (_a) {
58
+ return null;
59
+ }
60
+ }
61
+ function mergeSourceRef(target, source) {
62
+ if (!source) {
63
+ return;
64
+ }
65
+ const entries = Object.entries(source);
66
+ for (const [key, value] of entries) {
67
+ if (value === undefined || value === null) {
68
+ continue;
69
+ }
70
+ if (target[key] === undefined) {
71
+ target[key] = value;
72
+ }
73
+ }
74
+ }
50
75
  function applySearchParams(sourceRef, params) {
51
76
  const setIfEmpty = (key, value) => {
52
77
  if (sourceRef[key] !== undefined) {
@@ -62,26 +87,309 @@ function applySearchParams(sourceRef, params) {
62
87
  setIfEmpty('utmCampaign', params.get('utm_campaign'));
63
88
  setIfEmpty('utmTerm', params.get('utm_term'));
64
89
  setIfEmpty('utmContent', params.get('utm_content'));
90
+ setIfEmpty('utmId', params.get('utm_id'));
65
91
  setIfEmpty('ref', params.get('ref'));
92
+ setIfEmpty('gclid', params.get('gclid'));
93
+ setIfEmpty('fbclid', params.get('fbclid'));
94
+ setIfEmpty('msclkid', params.get('msclkid'));
95
+ setIfEmpty('ttclid', params.get('ttclid'));
96
+ setIfEmpty('twclid', params.get('twclid'));
97
+ setIfEmpty('liFatId', params.get('li_fat_id'));
98
+ }
99
+ function normalizeHost(host) {
100
+ if (!host) {
101
+ return null;
102
+ }
103
+ return host.trim().toLowerCase() || null;
104
+ }
105
+ function getRootDomain(host) {
106
+ const normalizedHost = normalizeHost(host);
107
+ if (!normalizedHost) {
108
+ return null;
109
+ }
110
+ const hostname = normalizedHost.split(':')[0];
111
+ if (hostname === 'localhost' || /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
112
+ return hostname;
113
+ }
114
+ const parts = hostname.split('.').filter(Boolean);
115
+ if (parts.length <= 2) {
116
+ return hostname;
117
+ }
118
+ return parts.slice(-2).join('.');
119
+ }
120
+ function isInternalReferer(landingHost, refererHost) {
121
+ const normalizedLandingHost = normalizeHost(landingHost);
122
+ const normalizedRefererHost = normalizeHost(refererHost);
123
+ if (!normalizedLandingHost || !normalizedRefererHost) {
124
+ return false;
125
+ }
126
+ if (normalizedLandingHost === normalizedRefererHost) {
127
+ return true;
128
+ }
129
+ return normalizedLandingHost.endsWith(`.${normalizedRefererHost}`)
130
+ || normalizedRefererHost.endsWith(`.${normalizedLandingHost}`);
131
+ }
132
+ function detectPlatform(value) {
133
+ const normalized = value === null || value === void 0 ? void 0 : value.trim().toLowerCase();
134
+ if (!normalized) {
135
+ return null;
136
+ }
137
+ const matcherList = [
138
+ { pattern: /chatgpt|chat-openai|openai/, platform: 'openai', channel: 'ai' },
139
+ { pattern: /claude|anthropic/, platform: 'anthropic', channel: 'ai' },
140
+ { pattern: /perplexity/, platform: 'perplexity', channel: 'ai' },
141
+ { pattern: /gemini/, platform: 'gemini', channel: 'ai' },
142
+ { pattern: /copilot/, platform: 'copilot', channel: 'ai' },
143
+ { pattern: /google/, platform: 'google', channel: 'search' },
144
+ { pattern: /bing/, platform: 'bing', channel: 'search' },
145
+ { pattern: /baidu/, platform: 'baidu', channel: 'search' },
146
+ { pattern: /yahoo/, platform: 'yahoo', channel: 'search' },
147
+ { pattern: /duckduckgo/, platform: 'duckduckgo', channel: 'search' },
148
+ { pattern: /facebook/, platform: 'facebook', channel: 'social' },
149
+ { pattern: /instagram/, platform: 'instagram', channel: 'social' },
150
+ { pattern: /x\.com|twitter/, platform: 'x', channel: 'social' },
151
+ { pattern: /linkedin/, platform: 'linkedin', channel: 'social' },
152
+ { pattern: /reddit/, platform: 'reddit', channel: 'social' },
153
+ { pattern: /youtube/, platform: 'youtube', channel: 'social' },
154
+ ];
155
+ const matched = matcherList.find(({ pattern }) => pattern.test(normalized));
156
+ if (!matched) {
157
+ return null;
158
+ }
159
+ return matched.platform;
160
+ }
161
+ function detectChannelFromPlatform(platform) {
162
+ switch (platform) {
163
+ case 'openai':
164
+ case 'anthropic':
165
+ case 'perplexity':
166
+ case 'gemini':
167
+ case 'copilot':
168
+ return 'ai';
169
+ case 'google':
170
+ case 'bing':
171
+ case 'baidu':
172
+ case 'yahoo':
173
+ case 'duckduckgo':
174
+ return 'search';
175
+ case 'facebook':
176
+ case 'instagram':
177
+ case 'x':
178
+ case 'linkedin':
179
+ case 'reddit':
180
+ case 'youtube':
181
+ return 'social';
182
+ default:
183
+ return null;
184
+ }
185
+ }
186
+ function parseUserAgent(request) {
187
+ var _a, _b, _c, _d, _e;
188
+ const userAgentHeader = request.headers.get('user-agent');
189
+ const secChUaMobile = (_a = normalizeQueryParam(request.headers.get('sec-ch-ua-mobile'))) !== null && _a !== void 0 ? _a : undefined;
190
+ const secChUaPlatform = (_b = normalizeQueryParam(request.headers.get('sec-ch-ua-platform'))) !== null && _b !== void 0 ? _b : undefined;
191
+ const userAgent = (_d = (_c = normalizeSourceRef(userAgentHeader)) === null || _c === void 0 ? void 0 : _c.slice(0, USER_AGENT_MAX_LENGTH)) !== null && _d !== void 0 ? _d : undefined;
192
+ const ua = (_e = userAgent === null || userAgent === void 0 ? void 0 : userAgent.toLowerCase()) !== null && _e !== void 0 ? _e : '';
193
+ let deviceType = 'desktop';
194
+ if (!ua) {
195
+ deviceType = 'unknown';
196
+ }
197
+ else if (/bot|spider|crawler|curl|wget|headless/.test(ua)) {
198
+ deviceType = 'bot';
199
+ }
200
+ else if (/ipad|tablet/.test(ua)) {
201
+ deviceType = 'tablet';
202
+ }
203
+ else if (/mobi|iphone|android/.test(ua) || secChUaMobile === '?1') {
204
+ deviceType = 'mobile';
205
+ }
206
+ let os = 'Unknown';
207
+ if (/iphone|ipad|ipod/.test(ua)) {
208
+ os = 'iOS';
209
+ }
210
+ else if (/android/.test(ua)) {
211
+ os = 'Android';
212
+ }
213
+ else if (/windows nt/.test(ua)) {
214
+ os = 'Windows';
215
+ }
216
+ else if (/mac os x|macintosh/.test(ua)) {
217
+ os = 'macOS';
218
+ }
219
+ else if (/cros/.test(ua)) {
220
+ os = 'Chrome OS';
221
+ }
222
+ else if (/linux/.test(ua)) {
223
+ os = 'Linux';
224
+ }
225
+ if (secChUaPlatform) {
226
+ const normalizedPlatform = secChUaPlatform.replaceAll('"', '');
227
+ if (normalizedPlatform && normalizedPlatform !== 'Unknown') {
228
+ os = normalizedPlatform;
229
+ }
230
+ }
231
+ let browser = 'Unknown';
232
+ if (/edg\//.test(ua)) {
233
+ browser = 'Edge';
234
+ }
235
+ else if (/opr\//.test(ua) || /opera/.test(ua)) {
236
+ browser = 'Opera';
237
+ }
238
+ else if (/samsungbrowser\//.test(ua)) {
239
+ browser = 'Samsung Internet';
240
+ }
241
+ else if (/crios\//.test(ua) || /chrome\//.test(ua)) {
242
+ browser = 'Chrome';
243
+ }
244
+ else if (/firefox\//.test(ua)) {
245
+ browser = 'Firefox';
246
+ }
247
+ else if (/safari\//.test(ua) && !/chrome\//.test(ua) && !/crios\//.test(ua)) {
248
+ browser = 'Safari';
249
+ }
250
+ return {
251
+ userAgent,
252
+ deviceType,
253
+ os,
254
+ browser,
255
+ secChUaMobile,
256
+ secChUaPlatform,
257
+ };
258
+ }
259
+ function parseFirstTouchHeader(request) {
260
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
261
+ const rawHeader = request.headers.get(FIRST_TOUCH_HEADER_NAME);
262
+ const normalizedHeader = (_a = normalizeSourceRef(rawHeader)) === null || _a === void 0 ? void 0 : _a.slice(0, FIRST_TOUCH_HEADER_MAX_LENGTH);
263
+ if (!normalizedHeader) {
264
+ return null;
265
+ }
266
+ const decodedHeader = decodeHeaderValue(normalizedHeader);
267
+ if (!decodedHeader) {
268
+ return null;
269
+ }
270
+ try {
271
+ const parsed = JSON.parse(decodedHeader);
272
+ const sourceRef = {};
273
+ sourceRef.capturedAt = (_b = normalizeQueryParam(typeof parsed.capturedAt === 'string' ? parsed.capturedAt : null)) !== null && _b !== void 0 ? _b : undefined;
274
+ sourceRef.landingUrl = (_c = normalizeSourceRef(typeof parsed.landingUrl === 'string' ? parsed.landingUrl : null)) !== null && _c !== void 0 ? _c : undefined;
275
+ sourceRef.landingPath = (_d = normalizeSourceRef(typeof parsed.landingPath === 'string' ? parsed.landingPath : null)) !== null && _d !== void 0 ? _d : undefined;
276
+ sourceRef.landingHost = (_e = normalizeHost(typeof parsed.landingHost === 'string' ? parsed.landingHost : null)) !== null && _e !== void 0 ? _e : undefined;
277
+ sourceRef.ref = (_f = normalizeQueryParam(typeof parsed.ref === 'string' ? parsed.ref : null)) !== null && _f !== void 0 ? _f : undefined;
278
+ sourceRef.utmSource = (_g = normalizeQueryParam(typeof parsed.utmSource === 'string' ? parsed.utmSource : null)) !== null && _g !== void 0 ? _g : undefined;
279
+ sourceRef.utmMedium = (_h = normalizeQueryParam(typeof parsed.utmMedium === 'string' ? parsed.utmMedium : null)) !== null && _h !== void 0 ? _h : undefined;
280
+ sourceRef.utmCampaign = (_j = normalizeQueryParam(typeof parsed.utmCampaign === 'string' ? parsed.utmCampaign : null)) !== null && _j !== void 0 ? _j : undefined;
281
+ sourceRef.utmTerm = (_k = normalizeQueryParam(typeof parsed.utmTerm === 'string' ? parsed.utmTerm : null)) !== null && _k !== void 0 ? _k : undefined;
282
+ sourceRef.utmContent = (_l = normalizeQueryParam(typeof parsed.utmContent === 'string' ? parsed.utmContent : null)) !== null && _l !== void 0 ? _l : undefined;
283
+ sourceRef.utmId = (_m = normalizeQueryParam(typeof parsed.utmId === 'string' ? parsed.utmId : null)) !== null && _m !== void 0 ? _m : undefined;
284
+ sourceRef.gclid = (_o = normalizeQueryParam(typeof parsed.gclid === 'string' ? parsed.gclid : null)) !== null && _o !== void 0 ? _o : undefined;
285
+ sourceRef.fbclid = (_p = normalizeQueryParam(typeof parsed.fbclid === 'string' ? parsed.fbclid : null)) !== null && _p !== void 0 ? _p : undefined;
286
+ sourceRef.msclkid = (_q = normalizeQueryParam(typeof parsed.msclkid === 'string' ? parsed.msclkid : null)) !== null && _q !== void 0 ? _q : undefined;
287
+ sourceRef.ttclid = (_r = normalizeQueryParam(typeof parsed.ttclid === 'string' ? parsed.ttclid : null)) !== null && _r !== void 0 ? _r : undefined;
288
+ sourceRef.twclid = (_s = normalizeQueryParam(typeof parsed.twclid === 'string' ? parsed.twclid : null)) !== null && _s !== void 0 ? _s : undefined;
289
+ sourceRef.liFatId = (_t = normalizeQueryParam(typeof parsed.liFatId === 'string' ? parsed.liFatId : null)) !== null && _t !== void 0 ? _t : undefined;
290
+ const externalReferrer = normalizeSourceRef(typeof parsed.externalReferrer === 'string' ? parsed.externalReferrer : null);
291
+ if (externalReferrer) {
292
+ sourceRef.httpRefer = externalReferrer;
293
+ try {
294
+ const refererUrl = new URL(externalReferrer);
295
+ sourceRef.refererHost = (_u = normalizeHost(refererUrl.host)) !== null && _u !== void 0 ? _u : undefined;
296
+ sourceRef.refererPath = (_v = normalizeSourceRef(refererUrl.pathname)) !== null && _v !== void 0 ? _v : undefined;
297
+ sourceRef.refererDomain = (_w = getRootDomain(refererUrl.host)) !== null && _w !== void 0 ? _w : undefined;
298
+ applySearchParams(sourceRef, refererUrl.searchParams);
299
+ }
300
+ catch (error) {
301
+ console.warn('Failed to parse first-touch referrer url:', error);
302
+ }
303
+ }
304
+ return Object.keys(sourceRef).length > 0 ? sourceRef : null;
305
+ }
306
+ catch (error) {
307
+ console.warn('Failed to parse first-touch header:', error);
308
+ return null;
309
+ }
310
+ }
311
+ function finalizeAttribution(sourceRef) {
312
+ var _a, _b, _c, _d;
313
+ const landingHost = normalizeHost(sourceRef.landingHost);
314
+ const refererHost = normalizeHost(sourceRef.refererHost);
315
+ const internal = isInternalReferer(landingHost, refererHost);
316
+ if (internal) {
317
+ sourceRef.isInternalReferer = true;
318
+ }
319
+ const utmPlatform = detectPlatform(sourceRef.utmSource) || detectPlatform(sourceRef.ref);
320
+ if (utmPlatform) {
321
+ sourceRef.sourcePlatform = utmPlatform;
322
+ sourceRef.sourceChannel = (_b = (_a = detectChannelFromPlatform(utmPlatform)) !== null && _a !== void 0 ? _a : sourceRef.sourceChannel) !== null && _b !== void 0 ? _b : 'campaign';
323
+ sourceRef.sourceType = 'campaign';
324
+ return;
325
+ }
326
+ if (sourceRef.gclid) {
327
+ sourceRef.sourcePlatform = 'google';
328
+ sourceRef.sourceChannel = 'search';
329
+ sourceRef.sourceType = 'campaign';
330
+ return;
331
+ }
332
+ if (sourceRef.msclkid) {
333
+ sourceRef.sourcePlatform = 'bing';
334
+ sourceRef.sourceChannel = 'search';
335
+ sourceRef.sourceType = 'campaign';
336
+ return;
337
+ }
338
+ if (sourceRef.fbclid) {
339
+ sourceRef.sourcePlatform = 'facebook';
340
+ sourceRef.sourceChannel = 'social';
341
+ sourceRef.sourceType = 'campaign';
342
+ return;
343
+ }
344
+ if (sourceRef.ttclid) {
345
+ sourceRef.sourcePlatform = 'tiktok';
346
+ sourceRef.sourceChannel = 'social';
347
+ sourceRef.sourceType = 'campaign';
348
+ return;
349
+ }
350
+ if (sourceRef.twclid) {
351
+ sourceRef.sourcePlatform = 'x';
352
+ sourceRef.sourceChannel = 'social';
353
+ sourceRef.sourceType = 'campaign';
354
+ return;
355
+ }
356
+ if (sourceRef.liFatId) {
357
+ sourceRef.sourcePlatform = 'linkedin';
358
+ sourceRef.sourceChannel = 'social';
359
+ sourceRef.sourceType = 'campaign';
360
+ return;
361
+ }
362
+ if (!internal && refererHost) {
363
+ const refererPlatform = detectPlatform(refererHost) || detectPlatform(sourceRef.httpRefer);
364
+ sourceRef.sourcePlatform = (_c = refererPlatform !== null && refererPlatform !== void 0 ? refererPlatform : getRootDomain(refererHost)) !== null && _c !== void 0 ? _c : refererHost;
365
+ sourceRef.sourceChannel = (_d = detectChannelFromPlatform(refererPlatform)) !== null && _d !== void 0 ? _d : 'referral';
366
+ sourceRef.sourceType = 'referer';
367
+ return;
368
+ }
369
+ sourceRef.sourcePlatform = 'direct';
370
+ sourceRef.sourceChannel = 'direct';
371
+ sourceRef.sourceType = 'direct';
66
372
  }
67
373
  // 提取用户首次访问来源
68
374
  function extractSourceRef(request) {
375
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
69
376
  const headerRef = request.headers.get('referer') || request.headers.get('referrer');
70
377
  const customRef = request.headers.get('x-source-ref');
71
378
  const queryRef = request.nextUrl.searchParams.get('ref');
72
- console.log({
73
- headerRef,
74
- customRef,
75
- queryRef
76
- });
77
- const sourceRef = {};
379
+ const firstTouchRef = parseFirstTouchHeader(request);
380
+ const sourceRef = Object.assign({}, parseUserAgent(request));
381
+ mergeSourceRef(sourceRef, firstTouchRef);
382
+ sourceRef.landingUrl = (_b = (_a = sourceRef.landingUrl) !== null && _a !== void 0 ? _a : normalizeSourceRef(request.nextUrl.toString())) !== null && _b !== void 0 ? _b : undefined;
383
+ sourceRef.landingPath = (_d = (_c = sourceRef.landingPath) !== null && _c !== void 0 ? _c : normalizeSourceRef(request.nextUrl.pathname)) !== null && _d !== void 0 ? _d : undefined;
384
+ sourceRef.landingHost = (_f = (_e = sourceRef.landingHost) !== null && _e !== void 0 ? _e : normalizeHost(request.nextUrl.host)) !== null && _f !== void 0 ? _f : undefined;
385
+ sourceRef.ref = (_h = (_g = sourceRef.ref) !== null && _g !== void 0 ? _g : normalizeQueryParam(queryRef)) !== null && _h !== void 0 ? _h : undefined;
78
386
  let normalizedHttpRef = null;
79
- const candidates = [headerRef, customRef, queryRef];
387
+ const candidates = [customRef, headerRef];
80
388
  for (const candidate of candidates) {
81
389
  const normalized = normalizeSourceRef(candidate);
82
390
  if (normalized) {
83
391
  normalizedHttpRef = normalized;
84
- sourceRef.httpRefer = normalized;
392
+ sourceRef.httpRefer = (_j = sourceRef.httpRefer) !== null && _j !== void 0 ? _j : normalized;
85
393
  break;
86
394
  }
87
395
  }
@@ -90,12 +398,16 @@ function extractSourceRef(request) {
90
398
  if (normalizedHttpRef) {
91
399
  try {
92
400
  const refererUrl = new URL(normalizedHttpRef);
401
+ sourceRef.refererHost = (_l = (_k = sourceRef.refererHost) !== null && _k !== void 0 ? _k : normalizeHost(refererUrl.host)) !== null && _l !== void 0 ? _l : undefined;
402
+ sourceRef.refererPath = (_o = (_m = sourceRef.refererPath) !== null && _m !== void 0 ? _m : normalizeSourceRef(refererUrl.pathname)) !== null && _o !== void 0 ? _o : undefined;
403
+ sourceRef.refererDomain = (_q = (_p = sourceRef.refererDomain) !== null && _p !== void 0 ? _p : getRootDomain(refererUrl.host)) !== null && _q !== void 0 ? _q : undefined;
93
404
  applySearchParams(sourceRef, refererUrl.searchParams);
94
405
  }
95
406
  catch (error) {
96
407
  console.warn('Failed to parse referer url for utm/ref:', error);
97
408
  }
98
409
  }
410
+ finalizeAttribution(sourceRef);
99
411
  return Object.keys(sourceRef).length > 0 ? sourceRef : null;
100
412
  }
101
413
  /**
package/dist/index.js CHANGED
@@ -85,7 +85,7 @@ exports.createCheckoutSession = stripeConfig.createCheckoutSession;
85
85
  exports.createCustomerPortalSession = stripeConfig.createCustomerPortalSession;
86
86
  exports.createOrGetCustomer = stripeConfig.createOrGetCustomer;
87
87
  exports.fetchPaymentId = stripeConfig.fetchPaymentId;
88
- exports.stripe = stripeConfig.stripe;
88
+ exports.getStripe = stripeConfig.getStripe;
89
89
  exports.updateSubscription = stripeConfig.updateSubscription;
90
90
  exports.validateStripeWebhook = stripeConfig.validateStripeWebhook;
91
91
  exports.ApiAuthUtils = authUtils.ApiAuthUtils;
package/dist/index.mjs CHANGED
@@ -16,7 +16,7 @@ export { getActiveProviderConfig, getCreditsFromPriceId, getPriceConfig, moneyPr
16
16
  export { getMoneyPriceInitUserContext } from './lib/money-price-helper.mjs';
17
17
  export { fingerprintConfig } from './lib/fingerprint-config.mjs';
18
18
  export { creditsConfig, freeAmount, freeExpiredDays, freeRegisterAmount, oneTimeExpiredDays } from './lib/credit-init.mjs';
19
- export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, stripe, updateSubscription, validateStripeWebhook } from './lib/stripe-config.mjs';
19
+ export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, getStripe, updateSubscription, validateStripeWebhook } from './lib/stripe-config.mjs';
20
20
  export { ApiAuthUtils, getAuthenticatedUser, requireAuth, requireAuthWithUser } from './lib/auth-utils.mjs';
21
21
  export { getQstash, getRedis, withQstash, withRedis } from './lib/upstash-config.mjs';
22
22
  export { acquireLock, releaseLock, withLock } from './lib/upstash/redis-lock.mjs';
package/dist/lib/index.js CHANGED
@@ -33,7 +33,7 @@ exports.createCheckoutSession = stripeConfig.createCheckoutSession;
33
33
  exports.createCustomerPortalSession = stripeConfig.createCustomerPortalSession;
34
34
  exports.createOrGetCustomer = stripeConfig.createOrGetCustomer;
35
35
  exports.fetchPaymentId = stripeConfig.fetchPaymentId;
36
- exports.stripe = stripeConfig.stripe;
36
+ exports.getStripe = stripeConfig.getStripe;
37
37
  exports.updateSubscription = stripeConfig.updateSubscription;
38
38
  exports.validateStripeWebhook = stripeConfig.validateStripeWebhook;
39
39
  exports.ApiAuthUtils = authUtils.ApiAuthUtils;
@@ -2,7 +2,7 @@ export { getActiveProviderConfig, getCreditsFromPriceId, getPriceConfig, moneyPr
2
2
  export { getMoneyPriceInitUserContext } from './money-price-helper.mjs';
3
3
  export { fingerprintConfig } from './fingerprint-config.mjs';
4
4
  export { creditsConfig, freeAmount, freeExpiredDays, freeRegisterAmount, oneTimeExpiredDays } from './credit-init.mjs';
5
- export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, stripe, updateSubscription, validateStripeWebhook } from './stripe-config.mjs';
5
+ export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, getStripe, updateSubscription, validateStripeWebhook } from './stripe-config.mjs';
6
6
  export { ApiAuthUtils, getAuthenticatedUser, requireAuth, requireAuthWithUser } from './auth-utils.mjs';
7
7
  export { getQstash, getRedis, withQstash, withRedis } from './upstash-config.mjs';
8
8
  export { acquireLock, releaseLock, withLock } from './upstash/redis-lock.mjs';
@@ -1,5 +1,5 @@
1
1
  import Stripe from 'stripe';
2
- export declare const stripe: Stripe;
2
+ export declare const getStripe: () => Stripe;
3
3
  export declare const validateStripeWebhook: (payload: string | Buffer, signature: string, secret: string) => Stripe.Event;
4
4
  export interface BasicCheckoutSessionParams {
5
5
  priceId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"stripe-config.d.ts","sourceRoot":"","sources":["../../src/lib/stripe-config.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAI5B,eAAO,MAAM,MAAM,QAEjB,CAAC;AAGH,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,GAAG,MAAM,EACxB,WAAW,MAAM,EACjB,QAAQ,MAAM,KACb,MAAM,CAAC,KAET,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAGnB;AAGD,eAAO,MAAM,qBAAqB,GAChC,QAAQ,0BAA0B,EAClC,mBAAmB,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,gBAAgB,KACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAgFjC,CAAC;AAGF,eAAO,MAAM,cAAc,GAAU,WAAW,MAAM,KAAI,OAAO,CAAC,MAAM,CAQvE,CAAA;AAGD,eAAO,MAAM,mBAAmB,GAAU,QAAQ;IAChD,MAAM,EAAE,MAAM,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CA0FjB,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,QAAQ;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,gBAAgB,CAAC;CACrE,KAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAiC9B,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAU,QAAQ;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,KAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAqBvC,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAC7B,gBAAgB,MAAM,EACtB,oBAAmB,OAAc,KAChC,OAAO,CAAC,MAAM,CAAC,YAAY,CAiC7B,CAAC;AAEF,qBAAa,6BAA8B,SAAQ,KAAK;;CAKvD"}
1
+ {"version":3,"file":"stripe-config.d.ts","sourceRoot":"","sources":["../../src/lib/stripe-config.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAK5B,eAAO,MAAM,SAAS,QAAO,MAa5B,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,GAAG,MAAM,EACxB,WAAW,MAAM,EACjB,QAAQ,MAAM,KACb,MAAM,CAAC,KAET,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAGnB;AAGD,eAAO,MAAM,qBAAqB,GAChC,QAAQ,0BAA0B,EAClC,mBAAmB,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,gBAAgB,KACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAgFjC,CAAC;AAGF,eAAO,MAAM,cAAc,GAAU,WAAW,MAAM,KAAI,OAAO,CAAC,MAAM,CAQvE,CAAA;AAGD,eAAO,MAAM,mBAAmB,GAAU,QAAQ;IAChD,MAAM,EAAE,MAAM,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CA0FjB,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,QAAQ;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,gBAAgB,CAAC;CACrE,KAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAiC9B,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAU,QAAQ;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,KAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAqBvC,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAC7B,gBAAgB,MAAM,EACtB,oBAAmB,OAAc,KAChC,OAAO,CAAC,MAAM,CAAC,YAAY,CAiC7B,CAAC;AAEF,qBAAa,6BAA8B,SAAQ,KAAK;;CAKvD"}
@@ -9,13 +9,22 @@ require('@prisma/client');
9
9
  require('../prisma/prisma.js');
10
10
  var apilog_service = require('../services/database/apilog.service.js');
11
11
 
12
- // Stripe Configuration
13
- const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
14
- apiVersion: '2025-11-17.clover',
15
- });
12
+ let stripeInstance = null;
13
+ const getStripe = () => {
14
+ const apiKey = process.env.STRIPE_SECRET_KEY;
15
+ if (!apiKey) {
16
+ throw new Error('STRIPE_SECRET_KEY is not configured');
17
+ }
18
+ if (!stripeInstance) {
19
+ stripeInstance = new Stripe(apiKey, {
20
+ apiVersion: '2025-11-17.clover',
21
+ });
22
+ }
23
+ return stripeInstance;
24
+ };
16
25
  // Helper function to validate webhook signature
17
26
  const validateStripeWebhook = (payload, signature, secret) => {
18
- return stripe.webhooks.constructEvent(payload, signature, secret);
27
+ return getStripe().webhooks.constructEvent(payload, signature, secret);
19
28
  };
20
29
  // Helper function to create checkout session
21
30
  const createCheckoutSession = (params, subscriptionData) => tslib_es6.__awaiter(void 0, void 0, void 0, function* () {
@@ -64,7 +73,7 @@ const createCheckoutSession = (params, subscriptionData) => tslib_es6.__awaiter(
64
73
  // Create log record with request
65
74
  const logId = yield apilog_service.Apilogger.logStripeOutgoing('createCheckoutSession', params);
66
75
  try {
67
- const session = yield stripe.checkout.sessions.create(sessionParams);
76
+ const session = yield getStripe().checkout.sessions.create(sessionParams);
68
77
  // Update log record with response
69
78
  apilog_service.Apilogger.updateResponse(logId, {
70
79
  session_id: session.id,
@@ -84,7 +93,7 @@ const createCheckoutSession = (params, subscriptionData) => tslib_es6.__awaiter(
84
93
  // 根据发票ID去查支付ID
85
94
  const fetchPaymentId = (invoiceId) => tslib_es6.__awaiter(void 0, void 0, void 0, function* () {
86
95
  var _a, _b;
87
- const fullInvoice = yield stripe.invoices.retrieve(invoiceId, {
96
+ const fullInvoice = yield getStripe().invoices.retrieve(invoiceId, {
88
97
  expand: ['payments']
89
98
  });
90
99
  const payment = (_a = fullInvoice.payments) === null || _a === void 0 ? void 0 : _a.data[0];
@@ -109,7 +118,7 @@ const createOrGetCustomer = (params) => tslib_es6.__awaiter(void 0, void 0, void
109
118
  });
110
119
  if (user.stripeCusId) {
111
120
  try {
112
- const customer = yield stripe.customers.retrieve(user.stripeCusId);
121
+ const customer = yield getStripe().customers.retrieve(user.stripeCusId);
113
122
  if ('deleted' in customer) {
114
123
  yield setStripeCustomerId(null);
115
124
  }
@@ -127,7 +136,7 @@ const createOrGetCustomer = (params) => tslib_es6.__awaiter(void 0, void 0, void
127
136
  }
128
137
  }
129
138
  if (user.email) {
130
- const existingCustomers = yield stripe.customers.list({
139
+ const existingCustomers = yield getStripe().customers.list({
131
140
  email: user.email,
132
141
  limit: 1,
133
142
  });
@@ -159,7 +168,7 @@ const createOrGetCustomer = (params) => tslib_es6.__awaiter(void 0, void 0, void
159
168
  name: customerParams.name,
160
169
  });
161
170
  try {
162
- const customer = yield stripe.customers.create(customerParams);
171
+ const customer = yield getStripe().customers.create(customerParams);
163
172
  yield setStripeCustomerId(customer.id);
164
173
  // Update log record with response
165
174
  apilog_service.Apilogger.updateResponse(logId, {
@@ -179,11 +188,11 @@ const createOrGetCustomer = (params) => tslib_es6.__awaiter(void 0, void 0, void
179
188
  // Helper function to update subscription
180
189
  const updateSubscription = (params) => tslib_es6.__awaiter(void 0, void 0, void 0, function* () {
181
190
  const { subscriptionId, priceId, prorationBehavior = 'create_prorations' } = params;
182
- const subscription = yield stripe.subscriptions.retrieve(subscriptionId);
191
+ const subscription = yield getStripe().subscriptions.retrieve(subscriptionId);
183
192
  // Create log record with request
184
193
  const logId = yield apilog_service.Apilogger.logStripeOutgoing('updateSubscription', params);
185
194
  try {
186
- const updatedSubscription = yield stripe.subscriptions.update(subscriptionId, {
195
+ const updatedSubscription = yield getStripe().subscriptions.update(subscriptionId, {
187
196
  items: [
188
197
  {
189
198
  id: subscription.items.data[0].id,
@@ -210,7 +219,7 @@ const updateSubscription = (params) => tslib_es6.__awaiter(void 0, void 0, void
210
219
  const createCustomerPortalSession = (params) => tslib_es6.__awaiter(void 0, void 0, void 0, function* () {
211
220
  const logId = yield apilog_service.Apilogger.logStripeOutgoing('createCustomerPortalSession', params);
212
221
  try {
213
- const session = yield stripe.billingPortal.sessions.create({
222
+ const session = yield getStripe().billingPortal.sessions.create({
214
223
  customer: params.customerId,
215
224
  return_url: params.returnUrl,
216
225
  });
@@ -237,12 +246,12 @@ const cancelSubscription = (subscriptionId_1, ...args_1) => tslib_es6.__awaiter(
237
246
  try {
238
247
  let result;
239
248
  if (cancelAtPeriodEnd) {
240
- result = yield stripe.subscriptions.update(subscriptionId, {
249
+ result = yield getStripe().subscriptions.update(subscriptionId, {
241
250
  cancel_at_period_end: true,
242
251
  });
243
252
  }
244
253
  else {
245
- result = yield stripe.subscriptions.cancel(subscriptionId);
254
+ result = yield getStripe().subscriptions.cancel(subscriptionId);
246
255
  }
247
256
  // Update log record with response
248
257
  apilog_service.Apilogger.updateResponse(logId, {
@@ -273,6 +282,6 @@ exports.createCheckoutSession = createCheckoutSession;
273
282
  exports.createCustomerPortalSession = createCustomerPortalSession;
274
283
  exports.createOrGetCustomer = createOrGetCustomer;
275
284
  exports.fetchPaymentId = fetchPaymentId;
276
- exports.stripe = stripe;
285
+ exports.getStripe = getStripe;
277
286
  exports.updateSubscription = updateSubscription;
278
287
  exports.validateStripeWebhook = validateStripeWebhook;
@@ -7,13 +7,22 @@ import '@prisma/client';
7
7
  import '../prisma/prisma.mjs';
8
8
  import { Apilogger } from '../services/database/apilog.service.mjs';
9
9
 
10
- // Stripe Configuration
11
- const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
12
- apiVersion: '2025-11-17.clover',
13
- });
10
+ let stripeInstance = null;
11
+ const getStripe = () => {
12
+ const apiKey = process.env.STRIPE_SECRET_KEY;
13
+ if (!apiKey) {
14
+ throw new Error('STRIPE_SECRET_KEY is not configured');
15
+ }
16
+ if (!stripeInstance) {
17
+ stripeInstance = new Stripe(apiKey, {
18
+ apiVersion: '2025-11-17.clover',
19
+ });
20
+ }
21
+ return stripeInstance;
22
+ };
14
23
  // Helper function to validate webhook signature
15
24
  const validateStripeWebhook = (payload, signature, secret) => {
16
- return stripe.webhooks.constructEvent(payload, signature, secret);
25
+ return getStripe().webhooks.constructEvent(payload, signature, secret);
17
26
  };
18
27
  // Helper function to create checkout session
19
28
  const createCheckoutSession = (params, subscriptionData) => __awaiter(void 0, void 0, void 0, function* () {
@@ -62,7 +71,7 @@ const createCheckoutSession = (params, subscriptionData) => __awaiter(void 0, vo
62
71
  // Create log record with request
63
72
  const logId = yield Apilogger.logStripeOutgoing('createCheckoutSession', params);
64
73
  try {
65
- const session = yield stripe.checkout.sessions.create(sessionParams);
74
+ const session = yield getStripe().checkout.sessions.create(sessionParams);
66
75
  // Update log record with response
67
76
  Apilogger.updateResponse(logId, {
68
77
  session_id: session.id,
@@ -82,7 +91,7 @@ const createCheckoutSession = (params, subscriptionData) => __awaiter(void 0, vo
82
91
  // 根据发票ID去查支付ID
83
92
  const fetchPaymentId = (invoiceId) => __awaiter(void 0, void 0, void 0, function* () {
84
93
  var _a, _b;
85
- const fullInvoice = yield stripe.invoices.retrieve(invoiceId, {
94
+ const fullInvoice = yield getStripe().invoices.retrieve(invoiceId, {
86
95
  expand: ['payments']
87
96
  });
88
97
  const payment = (_a = fullInvoice.payments) === null || _a === void 0 ? void 0 : _a.data[0];
@@ -107,7 +116,7 @@ const createOrGetCustomer = (params) => __awaiter(void 0, void 0, void 0, functi
107
116
  });
108
117
  if (user.stripeCusId) {
109
118
  try {
110
- const customer = yield stripe.customers.retrieve(user.stripeCusId);
119
+ const customer = yield getStripe().customers.retrieve(user.stripeCusId);
111
120
  if ('deleted' in customer) {
112
121
  yield setStripeCustomerId(null);
113
122
  }
@@ -125,7 +134,7 @@ const createOrGetCustomer = (params) => __awaiter(void 0, void 0, void 0, functi
125
134
  }
126
135
  }
127
136
  if (user.email) {
128
- const existingCustomers = yield stripe.customers.list({
137
+ const existingCustomers = yield getStripe().customers.list({
129
138
  email: user.email,
130
139
  limit: 1,
131
140
  });
@@ -157,7 +166,7 @@ const createOrGetCustomer = (params) => __awaiter(void 0, void 0, void 0, functi
157
166
  name: customerParams.name,
158
167
  });
159
168
  try {
160
- const customer = yield stripe.customers.create(customerParams);
169
+ const customer = yield getStripe().customers.create(customerParams);
161
170
  yield setStripeCustomerId(customer.id);
162
171
  // Update log record with response
163
172
  Apilogger.updateResponse(logId, {
@@ -177,11 +186,11 @@ const createOrGetCustomer = (params) => __awaiter(void 0, void 0, void 0, functi
177
186
  // Helper function to update subscription
178
187
  const updateSubscription = (params) => __awaiter(void 0, void 0, void 0, function* () {
179
188
  const { subscriptionId, priceId, prorationBehavior = 'create_prorations' } = params;
180
- const subscription = yield stripe.subscriptions.retrieve(subscriptionId);
189
+ const subscription = yield getStripe().subscriptions.retrieve(subscriptionId);
181
190
  // Create log record with request
182
191
  const logId = yield Apilogger.logStripeOutgoing('updateSubscription', params);
183
192
  try {
184
- const updatedSubscription = yield stripe.subscriptions.update(subscriptionId, {
193
+ const updatedSubscription = yield getStripe().subscriptions.update(subscriptionId, {
185
194
  items: [
186
195
  {
187
196
  id: subscription.items.data[0].id,
@@ -208,7 +217,7 @@ const updateSubscription = (params) => __awaiter(void 0, void 0, void 0, functio
208
217
  const createCustomerPortalSession = (params) => __awaiter(void 0, void 0, void 0, function* () {
209
218
  const logId = yield Apilogger.logStripeOutgoing('createCustomerPortalSession', params);
210
219
  try {
211
- const session = yield stripe.billingPortal.sessions.create({
220
+ const session = yield getStripe().billingPortal.sessions.create({
212
221
  customer: params.customerId,
213
222
  return_url: params.returnUrl,
214
223
  });
@@ -235,12 +244,12 @@ const cancelSubscription = (subscriptionId_1, ...args_1) => __awaiter(void 0, [s
235
244
  try {
236
245
  let result;
237
246
  if (cancelAtPeriodEnd) {
238
- result = yield stripe.subscriptions.update(subscriptionId, {
247
+ result = yield getStripe().subscriptions.update(subscriptionId, {
239
248
  cancel_at_period_end: true,
240
249
  });
241
250
  }
242
251
  else {
243
- result = yield stripe.subscriptions.cancel(subscriptionId);
252
+ result = yield getStripe().subscriptions.cancel(subscriptionId);
244
253
  }
245
254
  // Update log record with response
246
255
  Apilogger.updateResponse(logId, {
@@ -265,4 +274,4 @@ class ActiveSubscriptionExistsError extends Error {
265
274
  }
266
275
  }
267
276
 
268
- export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, stripe, updateSubscription, validateStripeWebhook };
277
+ export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, getStripe, updateSubscription, validateStripeWebhook };