@quantabit/pricing-sdk 1.0.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/dist/index.cjs ADDED
@@ -0,0 +1,667 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var sdkConfig = require('@quantabit/sdk-config');
7
+ var React = require('react');
8
+
9
+ /**
10
+ * Pricing SDK - API 客户端
11
+ * 定价管理系统后端接口封装
12
+ *
13
+ * 使用 BaseApiClient 基类简化代码
14
+ */
15
+
16
+
17
+ /**
18
+ * 定价 API 客户端
19
+ */
20
+ class PricingApiClient extends sdkConfig.BaseApiClient {
21
+ constructor(config = {}) {
22
+ super('/pricing', config);
23
+ }
24
+
25
+ // ============ 价格表 ============
26
+
27
+ /**
28
+ * 获取价格表列表
29
+ * @param {Object} params - 查询参数
30
+ */
31
+ async getPricingTables(params = {}) {
32
+ return this.get('/tables', params);
33
+ }
34
+
35
+ /**
36
+ * 获取价格表详情
37
+ * @param {string} tableId - 价格表 ID
38
+ */
39
+ async getPricingTable(tableId) {
40
+ return this.get(`/tables/${tableId}`);
41
+ }
42
+
43
+ /**
44
+ * 获取产品当前价格
45
+ * @param {string} productId - 产品 ID
46
+ */
47
+ async getProductPrice(productId) {
48
+ return this.get(`/products/${productId}/price`);
49
+ }
50
+
51
+ // ============ 套餐定价 ============
52
+
53
+ /**
54
+ * 获取套餐列表
55
+ * @param {string} category - 分类
56
+ */
57
+ async getPlans(category) {
58
+ return this.get('/plans', {
59
+ category
60
+ });
61
+ }
62
+
63
+ /**
64
+ * 获取套餐详情
65
+ * @param {string} planId - 套餐 ID
66
+ */
67
+ async getPlan(planId) {
68
+ return this.get(`/plans/${planId}`);
69
+ }
70
+
71
+ /**
72
+ * 比较套餐
73
+ * @param {string[]} planIds - 套餐 ID 列表
74
+ */
75
+ async comparePlans(planIds) {
76
+ return this.post('/plans/compare', {
77
+ plan_ids: planIds
78
+ });
79
+ }
80
+
81
+ // ============ 折扣与优惠 ============
82
+
83
+ /**
84
+ * 获取可用折扣
85
+ * @param {string} productId - 产品 ID
86
+ */
87
+ async getDiscounts(productId) {
88
+ return this.get(`/products/${productId}/discounts`);
89
+ }
90
+
91
+ /**
92
+ * 验证优惠码
93
+ * @param {string} code - 优惠码
94
+ * @param {string} productId - 产品 ID
95
+ */
96
+ async validateCoupon(code, productId) {
97
+ return this.post('/coupon/validate', {
98
+ code,
99
+ product_id: productId
100
+ });
101
+ }
102
+
103
+ /**
104
+ * 计算最终价格
105
+ * @param {Object} priceRequest - 价格计算请求
106
+ */
107
+ async calculatePrice(priceRequest) {
108
+ return this.post('/calculate', priceRequest);
109
+ }
110
+
111
+ // ============ 货币与汇率 ============
112
+
113
+ /**
114
+ * 获取支持的货币
115
+ */
116
+ async getSupportedCurrencies() {
117
+ return this.get('/currencies');
118
+ }
119
+
120
+ /**
121
+ * 获取汇率
122
+ * @param {string} from - 源货币
123
+ * @param {string} to - 目标货币
124
+ */
125
+ async getExchangeRate(from, to) {
126
+ return this.get('/exchange-rate', {
127
+ from,
128
+ to
129
+ });
130
+ }
131
+
132
+ /**
133
+ * 转换货币
134
+ * @param {number} amount - 金额
135
+ * @param {string} from - 源货币
136
+ * @param {string} to - 目标货币
137
+ */
138
+ async convertCurrency(amount, from, to) {
139
+ return this.post('/convert', {
140
+ amount,
141
+ from,
142
+ to
143
+ });
144
+ }
145
+
146
+ // ============ 订阅定价 ============
147
+
148
+ /**
149
+ * 获取订阅价格
150
+ * @param {string} planId - 套餐 ID
151
+ * @param {string} billingCycle - 计费周期
152
+ */
153
+ async getSubscriptionPrice(planId, billingCycle = 'monthly') {
154
+ return this.get(`/subscriptions/${planId}/price`, {
155
+ billing_cycle: billingCycle
156
+ });
157
+ }
158
+
159
+ /**
160
+ * 计算升级/降级价格
161
+ * @param {string} currentPlanId - 当前套餐
162
+ * @param {string} targetPlanId - 目标套餐
163
+ */
164
+ async calculateUpgradePrice(currentPlanId, targetPlanId) {
165
+ return this.post('/subscriptions/upgrade-price', {
166
+ current_plan_id: currentPlanId,
167
+ target_plan_id: targetPlanId
168
+ });
169
+ }
170
+
171
+ // ============ 价格历史 ============
172
+
173
+ /**
174
+ * 获取价格历史
175
+ * @param {string} productId - 产品 ID
176
+ * @param {Object} params - 查询参数
177
+ */
178
+ async getPriceHistory(productId, params = {}) {
179
+ return this.get(`/products/${productId}/history`, params);
180
+ }
181
+ }
182
+
183
+ // 创建默认实例
184
+ const pricingApi = new PricingApiClient();
185
+
186
+ /**
187
+ * Pricing SDK - React Hooks
188
+ */
189
+
190
+ function usePricing() {
191
+ const [plans, setPlans] = React.useState([]);
192
+ const [currencies, setCurrencies] = React.useState([]);
193
+ const [loading, setLoading] = React.useState(false);
194
+ const [error, setError] = React.useState(null);
195
+ const fetchPlans = React.useCallback(async category => {
196
+ setLoading(true);
197
+ try {
198
+ const result = await pricingApi.getPlans(category);
199
+ setPlans(result.data || []);
200
+ } catch (err) {
201
+ setError(err.message);
202
+ } finally {
203
+ setLoading(false);
204
+ }
205
+ }, []);
206
+ const fetchCurrencies = React.useCallback(async () => {
207
+ try {
208
+ const result = await pricingApi.getSupportedCurrencies();
209
+ setCurrencies(result.data || []);
210
+ } catch (err) {
211
+ setError(err.message);
212
+ }
213
+ }, []);
214
+ const calculatePrice = React.useCallback(async priceRequest => {
215
+ try {
216
+ return await pricingApi.calculatePrice(priceRequest);
217
+ } catch (err) {
218
+ setError(err.message);
219
+ throw err;
220
+ }
221
+ }, []);
222
+ const validateCoupon = React.useCallback(async (code, productId) => {
223
+ try {
224
+ return await pricingApi.validateCoupon(code, productId);
225
+ } catch (err) {
226
+ setError(err.message);
227
+ throw err;
228
+ }
229
+ }, []);
230
+ const comparePlans = React.useCallback(async planIds => {
231
+ try {
232
+ return await pricingApi.comparePlans(planIds);
233
+ } catch (err) {
234
+ setError(err.message);
235
+ throw err;
236
+ }
237
+ }, []);
238
+ React.useEffect(() => {
239
+ fetchPlans();
240
+ fetchCurrencies();
241
+ }, [fetchPlans, fetchCurrencies]);
242
+ return {
243
+ plans,
244
+ currencies,
245
+ loading,
246
+ error,
247
+ fetchPlans,
248
+ calculatePrice,
249
+ validateCoupon,
250
+ comparePlans
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Pricing SDK - 定价组件
256
+ */
257
+
258
+
259
+ /**
260
+ * Pricing SDK - PricingTable
261
+ * 企业级高级版 SaaS 定价表单
262
+ */
263
+ function PricingTable({
264
+ onSelect
265
+ }) {
266
+ const {
267
+ plans,
268
+ loading,
269
+ validateCoupon
270
+ } = usePricing();
271
+ const [billingCycle, setBillingCycle] = React.useState('monthly');
272
+ const [couponCode, setCouponCode] = React.useState('');
273
+ const [couponDiscount, setCouponDiscount] = React.useState(null);
274
+ const handleApplyCoupon = async () => {
275
+ if (!couponCode) return;
276
+ try {
277
+ const result = await validateCoupon(couponCode, null);
278
+ if (result.data?.valid) {
279
+ setCouponDiscount(result.data.discount);
280
+ }
281
+ } catch (err) {
282
+ // 错误处理
283
+ }
284
+ };
285
+ if (loading) {
286
+ return /*#__PURE__*/React.createElement("div", {
287
+ style: {
288
+ padding: '60px',
289
+ textAlign: 'center',
290
+ color: '#64748B'
291
+ }
292
+ }, /*#__PURE__*/React.createElement("div", {
293
+ className: "eco-loading-spinner",
294
+ style: {
295
+ width: '32px',
296
+ height: '32px',
297
+ border: '3px solid #E2E8F0',
298
+ borderTopColor: '#3B82F6',
299
+ borderRadius: '50%',
300
+ animation: 'spin 1s linear infinite',
301
+ margin: '0 auto 16px'
302
+ }
303
+ }), "Loading pricing plans...");
304
+ }
305
+ return /*#__PURE__*/React.createElement("div", {
306
+ className: "eco-pricing-table",
307
+ style: {
308
+ width: '100%',
309
+ maxWidth: '1200px',
310
+ margin: '0 auto',
311
+ padding: '40px 20px'
312
+ }
313
+ }, /*#__PURE__*/React.createElement("div", {
314
+ style: {
315
+ display: 'flex',
316
+ justifyContent: 'center',
317
+ marginBottom: '48px'
318
+ }
319
+ }, /*#__PURE__*/React.createElement("div", {
320
+ style: {
321
+ display: 'inline-flex',
322
+ background: 'rgba(241, 245, 249, 0.8)',
323
+ padding: '6px',
324
+ borderRadius: '16px',
325
+ backdropFilter: 'blur(10px)',
326
+ border: '1px solid #E2E8F0'
327
+ }
328
+ }, /*#__PURE__*/React.createElement("button", {
329
+ onClick: () => setBillingCycle('monthly'),
330
+ style: {
331
+ padding: '10px 24px',
332
+ border: 'none',
333
+ borderRadius: '12px',
334
+ fontSize: '15px',
335
+ fontWeight: 600,
336
+ background: billingCycle === 'monthly' ? '#fff' : 'transparent',
337
+ color: billingCycle === 'monthly' ? '#0F172A' : '#64748B',
338
+ boxShadow: billingCycle === 'monthly' ? '0 4px 12px rgba(0,0,0,0.05)' : 'none',
339
+ cursor: 'pointer',
340
+ transition: 'all 0.3s ease'
341
+ }
342
+ }, "Monthly"), /*#__PURE__*/React.createElement("button", {
343
+ onClick: () => setBillingCycle('yearly'),
344
+ style: {
345
+ padding: '10px 24px',
346
+ border: 'none',
347
+ borderRadius: '12px',
348
+ fontSize: '15px',
349
+ fontWeight: 600,
350
+ background: billingCycle === 'yearly' ? '#fff' : 'transparent',
351
+ color: billingCycle === 'yearly' ? '#0F172A' : '#64748B',
352
+ boxShadow: billingCycle === 'yearly' ? '0 4px 12px rgba(0,0,0,0.05)' : 'none',
353
+ cursor: 'pointer',
354
+ transition: 'all 0.3s ease',
355
+ display: 'flex',
356
+ alignItems: 'center',
357
+ gap: '8px'
358
+ }
359
+ }, "Yearly", /*#__PURE__*/React.createElement("span", {
360
+ style: {
361
+ background: '#DCFCE7',
362
+ color: '#166534',
363
+ padding: '2px 8px',
364
+ borderRadius: '20px',
365
+ fontSize: '12px',
366
+ fontWeight: 700
367
+ }
368
+ }, "Save 20%")))), /*#__PURE__*/React.createElement("div", {
369
+ style: {
370
+ display: 'grid',
371
+ gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))',
372
+ gap: '32px',
373
+ alignItems: 'flex-start'
374
+ }
375
+ }, plans.map(plan => /*#__PURE__*/React.createElement("div", {
376
+ key: plan.id,
377
+ style: {
378
+ position: 'relative',
379
+ background: plan.popular ? 'linear-gradient(145deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.6) 100%)' : 'rgba(255, 255, 255, 0.4)',
380
+ backdropFilter: 'blur(20px)',
381
+ WebkitBackdropFilter: 'blur(20px)',
382
+ border: plan.popular ? '2px solid #3B82F6' : '1px solid rgba(255, 255, 255, 0.5)',
383
+ borderRadius: '24px',
384
+ padding: '40px 32px',
385
+ boxShadow: plan.popular ? '0 20px 40px -10px rgba(59, 130, 246, 0.15)' : '0 10px 30px -10px rgba(0,0,0,0.05)',
386
+ transform: plan.popular ? 'scale(1.02)' : 'scale(1)',
387
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
388
+ display: 'flex',
389
+ flexDirection: 'column',
390
+ gap: '24px'
391
+ },
392
+ onMouseEnter: e => e.currentTarget.style.transform = plan.popular ? 'scale(1.04) translateY(-4px)' : 'scale(1.02) translateY(-4px)',
393
+ onMouseLeave: e => e.currentTarget.style.transform = plan.popular ? 'scale(1.02)' : 'scale(1)'
394
+ }, plan.popular && /*#__PURE__*/React.createElement("div", {
395
+ style: {
396
+ position: 'absolute',
397
+ top: '-16px',
398
+ left: '50%',
399
+ transform: 'translateX(-50%)',
400
+ background: 'linear-gradient(135deg, #3B82F6 0%, #2563EB 100%)',
401
+ color: '#fff',
402
+ padding: '6px 16px',
403
+ borderRadius: '20px',
404
+ fontSize: '13px',
405
+ fontWeight: 700,
406
+ boxShadow: '0 4px 12px rgba(59, 130, 246, 0.3)'
407
+ }
408
+ }, "MOST POPULAR"), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", {
409
+ style: {
410
+ fontSize: '22px',
411
+ fontWeight: 700,
412
+ margin: '0 0 12px',
413
+ color: '#0F172A'
414
+ }
415
+ }, plan.name), /*#__PURE__*/React.createElement("p", {
416
+ style: {
417
+ margin: 0,
418
+ color: '#64748B',
419
+ fontSize: '15px',
420
+ lineHeight: 1.5
421
+ }
422
+ }, plan.description)), /*#__PURE__*/React.createElement("div", {
423
+ style: {
424
+ display: 'flex',
425
+ alignItems: 'baseline',
426
+ gap: '4px'
427
+ }
428
+ }, /*#__PURE__*/React.createElement("span", {
429
+ style: {
430
+ fontSize: '48px',
431
+ fontWeight: 800,
432
+ color: '#0F172A',
433
+ letterSpacing: '-1px'
434
+ }
435
+ }, "\xA5", billingCycle === 'yearly' ? plan.yearly_price : plan.monthly_price), /*#__PURE__*/React.createElement("span", {
436
+ style: {
437
+ color: '#64748B',
438
+ fontWeight: 500
439
+ }
440
+ }, "/", billingCycle === 'yearly' ? 'year' : 'mo')), /*#__PURE__*/React.createElement("button", {
441
+ onClick: () => onSelect?.(plan, billingCycle),
442
+ style: {
443
+ width: '100%',
444
+ padding: '16px',
445
+ borderRadius: '14px',
446
+ border: 'none',
447
+ background: plan.popular ? '#0F172A' : '#F1F5F9',
448
+ color: plan.popular ? '#fff' : '#0F172A',
449
+ fontSize: '15px',
450
+ fontWeight: 700,
451
+ cursor: 'pointer',
452
+ transition: 'all 0.2s ease',
453
+ boxShadow: plan.popular ? '0 4px 12px rgba(15, 23, 42, 0.2)' : 'none'
454
+ },
455
+ onMouseEnter: e => e.currentTarget.style.opacity = '0.9',
456
+ onMouseLeave: e => e.currentTarget.style.opacity = '1'
457
+ }, "Get Started"), /*#__PURE__*/React.createElement("ul", {
458
+ style: {
459
+ listStyle: 'none',
460
+ padding: 0,
461
+ margin: 0,
462
+ display: 'flex',
463
+ flexDirection: 'column',
464
+ gap: '16px'
465
+ }
466
+ }, plan.features?.map((feature, i) => /*#__PURE__*/React.createElement("li", {
467
+ key: i,
468
+ style: {
469
+ display: 'flex',
470
+ alignItems: 'center',
471
+ gap: '12px',
472
+ fontSize: '15px',
473
+ color: '#334155'
474
+ }
475
+ }, /*#__PURE__*/React.createElement("div", {
476
+ style: {
477
+ width: '20px',
478
+ height: '20px',
479
+ borderRadius: '50%',
480
+ background: '#DBEAFE',
481
+ color: '#3B82F6',
482
+ display: 'flex',
483
+ alignItems: 'center',
484
+ justifyContent: 'center',
485
+ fontSize: '12px',
486
+ fontWeight: 900
487
+ }
488
+ }, "\u2713"), feature)))))), /*#__PURE__*/React.createElement("div", {
489
+ style: {
490
+ marginTop: '48px',
491
+ display: 'flex',
492
+ justifyContent: 'center'
493
+ }
494
+ }, /*#__PURE__*/React.createElement("div", {
495
+ style: {
496
+ display: 'flex',
497
+ gap: '8px',
498
+ background: '#fff',
499
+ padding: '6px',
500
+ borderRadius: '16px',
501
+ border: '1px solid #E2E8F0',
502
+ boxShadow: '0 4px 20px rgba(0,0,0,0.03)'
503
+ }
504
+ }, /*#__PURE__*/React.createElement("input", {
505
+ type: "text",
506
+ placeholder: "Have a promo code?",
507
+ value: couponCode,
508
+ onChange: e => setCouponCode(e.target.value),
509
+ style: {
510
+ border: 'none',
511
+ outline: 'none',
512
+ padding: '10px 16px',
513
+ width: '200px',
514
+ fontSize: '14px',
515
+ background: 'transparent'
516
+ }
517
+ }), /*#__PURE__*/React.createElement("button", {
518
+ onClick: handleApplyCoupon,
519
+ style: {
520
+ background: '#F1F5F9',
521
+ color: '#334155',
522
+ border: 'none',
523
+ padding: '10px 20px',
524
+ borderRadius: '10px',
525
+ fontSize: '14px',
526
+ fontWeight: 600,
527
+ cursor: 'pointer'
528
+ }
529
+ }, "Apply"))), couponDiscount && /*#__PURE__*/React.createElement("div", {
530
+ style: {
531
+ textAlign: 'center',
532
+ marginTop: '16px',
533
+ color: '#166534',
534
+ fontWeight: 600,
535
+ fontSize: '15px'
536
+ }
537
+ }, "\u2728 Promo code applied! You save ", couponDiscount, "%."));
538
+ }
539
+
540
+ /**
541
+ * Pricing SDK - 国际化
542
+ */
543
+
544
+ const zh = {
545
+ pricing: {
546
+ title: '定价',
547
+ subtitle: '选择适合您的套餐',
548
+ plans: {
549
+ title: '套餐',
550
+ free: '免费版',
551
+ basic: '基础版',
552
+ pro: '专业版',
553
+ enterprise: '企业版'
554
+ },
555
+ billing: {
556
+ monthly: '月付',
557
+ yearly: '年付',
558
+ save: '节省'
559
+ },
560
+ features: {
561
+ title: '功能特性',
562
+ included: '包含',
563
+ notIncluded: '不包含'
564
+ },
565
+ actions: {
566
+ select: '选择套餐',
567
+ upgrade: '升级',
568
+ downgrade: '降级',
569
+ compare: '对比套餐'
570
+ },
571
+ coupon: {
572
+ title: '优惠码',
573
+ placeholder: '输入优惠码',
574
+ apply: '应用',
575
+ valid: '优惠码有效',
576
+ invalid: '优惠码无效'
577
+ },
578
+ currency: {
579
+ title: '货币',
580
+ select: '选择货币'
581
+ },
582
+ messages: {
583
+ planSelected: '套餐已选择',
584
+ couponApplied: '优惠码已应用'
585
+ },
586
+ errors: {
587
+ fetchFailed: '获取定价失败',
588
+ invalidCoupon: '无效的优惠码'
589
+ }
590
+ }
591
+ };
592
+ const en = {
593
+ pricing: {
594
+ title: 'Pricing',
595
+ subtitle: 'Choose the plan that fits you',
596
+ plans: {
597
+ title: 'Plans',
598
+ free: 'Free',
599
+ basic: 'Basic',
600
+ pro: 'Pro',
601
+ enterprise: 'Enterprise'
602
+ },
603
+ billing: {
604
+ monthly: 'Monthly',
605
+ yearly: 'Yearly',
606
+ save: 'Save'
607
+ },
608
+ features: {
609
+ title: 'Features',
610
+ included: 'Included',
611
+ notIncluded: 'Not Included'
612
+ },
613
+ actions: {
614
+ select: 'Select Plan',
615
+ upgrade: 'Upgrade',
616
+ downgrade: 'Downgrade',
617
+ compare: 'Compare Plans'
618
+ },
619
+ coupon: {
620
+ title: 'Coupon Code',
621
+ placeholder: 'Enter coupon code',
622
+ apply: 'Apply',
623
+ valid: 'Coupon valid',
624
+ invalid: 'Coupon invalid'
625
+ },
626
+ currency: {
627
+ title: 'Currency',
628
+ select: 'Select Currency'
629
+ },
630
+ messages: {
631
+ planSelected: 'Plan selected',
632
+ couponApplied: 'Coupon applied'
633
+ },
634
+ errors: {
635
+ fetchFailed: 'Failed to fetch pricing',
636
+ invalidCoupon: 'Invalid coupon code'
637
+ }
638
+ }
639
+ };
640
+
641
+ /**
642
+ * @quantabit/pricing-sdk - Pricing SDK
643
+ */
644
+
645
+ // Type Definitions
646
+ const BillingCycle = {
647
+ MONTHLY: 'monthly',
648
+ YEARLY: 'yearly',
649
+ QUARTERLY: 'quarterly'
650
+ };
651
+ const PlanType = {
652
+ FREE: 'free',
653
+ BASIC: 'basic',
654
+ PRO: 'pro',
655
+ ENTERPRISE: 'enterprise'
656
+ };
657
+
658
+ exports.BillingCycle = BillingCycle;
659
+ exports.PlanType = PlanType;
660
+ exports.PricingApiClient = PricingApiClient;
661
+ exports.PricingTable = PricingTable;
662
+ exports.default = pricingApi;
663
+ exports.en = en;
664
+ exports.pricingApi = pricingApi;
665
+ exports.usePricing = usePricing;
666
+ exports.zh = zh;
667
+ //# sourceMappingURL=index.cjs.map