@windrun-huaiin/third-ui 7.3.3 → 7.3.4
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/clerk/fingerprint/fingerprint-provider.js +26 -6
- package/dist/clerk/fingerprint/fingerprint-provider.mjs +27 -7
- package/dist/clerk/fingerprint/use-fingerprint.js +1 -1
- package/dist/clerk/fingerprint/use-fingerprint.mjs +1 -1
- package/dist/main/faq.js +0 -1
- package/dist/main/faq.mjs +0 -1
- package/dist/main/gallery.js +0 -1
- package/dist/main/gallery.mjs +0 -1
- package/dist/main/money-price/money-price-config-util.d.ts +7 -0
- package/dist/main/money-price/money-price-config-util.js +19 -0
- package/dist/main/money-price/money-price-config-util.mjs +16 -0
- package/dist/main/money-price/money-price-interactive.js +28 -6
- package/dist/main/money-price/money-price-interactive.mjs +26 -4
- package/dist/main/money-price/money-price-types.d.ts +1 -1
- package/dist/main/money-price/money-price.js +2 -2
- package/dist/main/money-price/money-price.mjs +1 -1
- package/dist/main/price-plan.js +0 -1
- package/dist/main/price-plan.mjs +0 -1
- package/dist/main/server.d.ts +1 -1
- package/dist/main/server.js +3 -4
- package/dist/main/server.mjs +1 -1
- package/dist/node_modules/.pnpm/cose-base@1.0.3/node_modules/cose-base/cose-base.mjs +1 -1
- package/dist/node_modules/.pnpm/cose-base@2.2.0/node_modules/cose-base/cose-base.mjs +1 -1
- package/package.json +1 -1
- package/src/clerk/fingerprint/fingerprint-provider.tsx +53 -20
- package/src/clerk/fingerprint/use-fingerprint.ts +1 -1
- package/src/main/money-price/money-price-config-util.ts +23 -0
- package/src/main/money-price/money-price-interactive.tsx +28 -3
- package/src/main/money-price/money-price-types.ts +1 -1
- package/src/main/money-price/money-price.tsx +1 -1
- package/src/main/server.ts +1 -2
- package/src/main/money-price/money-price-config.ts +0 -229
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var useFingerprint = require('./use-fingerprint.js');
|
|
7
|
+
var server = require('@windrun-huaiin/base-ui/components/server');
|
|
7
8
|
|
|
8
9
|
const FingerprintContext = React.createContext(undefined);
|
|
9
10
|
/**
|
|
@@ -47,9 +48,29 @@ function withFingerprint(Component, config) {
|
|
|
47
48
|
function FingerprintStatus() {
|
|
48
49
|
const { fingerprintId, xUser, xCredit, xSubscription, isLoading, isInitialized, error } = useFingerprintContext();
|
|
49
50
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
51
|
+
const modalRef = React.useRef(null);
|
|
50
52
|
const handleToggle = () => {
|
|
51
53
|
setIsOpen(!isOpen);
|
|
52
54
|
};
|
|
55
|
+
const handleBackdropClick = (e) => {
|
|
56
|
+
if (e.target === e.currentTarget) {
|
|
57
|
+
setIsOpen(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
// ESC键关闭弹框
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
const handleEscKey = (e) => {
|
|
63
|
+
if (e.key === 'Escape' && isOpen) {
|
|
64
|
+
setIsOpen(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
if (isOpen) {
|
|
68
|
+
document.addEventListener('keydown', handleEscKey);
|
|
69
|
+
}
|
|
70
|
+
return () => {
|
|
71
|
+
document.removeEventListener('keydown', handleEscKey);
|
|
72
|
+
};
|
|
73
|
+
}, [isOpen]);
|
|
53
74
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { onClick: handleToggle, style: {
|
|
54
75
|
position: 'fixed',
|
|
55
76
|
top: '10px',
|
|
@@ -65,12 +86,11 @@ function FingerprintStatus() {
|
|
|
65
86
|
alignItems: 'center',
|
|
66
87
|
justifyContent: 'center',
|
|
67
88
|
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
|
|
68
|
-
}, children: jsxRuntime.jsx(
|
|
69
|
-
fontSize: '24px',
|
|
89
|
+
}, children: jsxRuntime.jsx(server.globalLucideIcons.BTC, { size: 24, style: {
|
|
70
90
|
color: 'white',
|
|
71
91
|
transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
72
92
|
transition: 'transform 0.3s ease',
|
|
73
|
-
}
|
|
93
|
+
} }) }), isOpen && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { onClick: handleBackdropClick, style: {
|
|
74
94
|
position: 'fixed',
|
|
75
95
|
top: 0,
|
|
76
96
|
left: 0,
|
|
@@ -78,7 +98,7 @@ function FingerprintStatus() {
|
|
|
78
98
|
height: '100%',
|
|
79
99
|
background: 'rgba(0, 0, 0, 0.5)',
|
|
80
100
|
zIndex: 9998,
|
|
81
|
-
} }), jsxRuntime.jsxs("div", { style: {
|
|
101
|
+
} }), jsxRuntime.jsxs("div", { ref: modalRef, style: {
|
|
82
102
|
position: 'fixed',
|
|
83
103
|
top: '70px',
|
|
84
104
|
left: '10px',
|
|
@@ -87,11 +107,11 @@ function FingerprintStatus() {
|
|
|
87
107
|
borderRadius: '5px',
|
|
88
108
|
fontSize: '12px',
|
|
89
109
|
fontFamily: 'monospace',
|
|
90
|
-
maxWidth: '
|
|
110
|
+
maxWidth: '500px',
|
|
91
111
|
zIndex: 9999,
|
|
92
112
|
border: '1px solid #ccc',
|
|
93
113
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)',
|
|
94
|
-
}, children: [jsxRuntime.jsx("h4", { style: { margin: '0 0 5px 0' }, children: "Fingerprint Debug" }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "FP_ID:" }), " ", fingerprintId || 'None'] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "Loading:" }), " ", isLoading ? 'Yes' : 'No'] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "Initialized:" }), " ", isInitialized ? 'Yes' : 'No'] }), error && jsxRuntime.jsxs("div", { style: { color: 'red' }, children: [jsxRuntime.jsx("strong", { children: "Error:" }), " ", error] }), xUser && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "user_id:" }), " ", xUser.userId, " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "clerk_user_id:" }), " ", xUser.clerkUserId || 'None', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "email:" }), " ", xUser.email || 'None', " ", jsxRuntime.jsx("br", {})] })), xCredit && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "Credits:" }), " ", xCredit.balanceFree, " Free + ", xCredit.balancePaid, " Paid = ", xCredit.totalBalance, " Total"] })), xSubscription
|
|
114
|
+
}, children: [jsxRuntime.jsx("h4", { style: { margin: '0 0 5px 0' }, children: "Fingerprint Debug" }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "FP_ID:" }), " ", fingerprintId || 'None'] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "Loading:" }), " ", isLoading ? 'Yes' : 'No'] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "Initialized:" }), " ", isInitialized ? 'Yes' : 'No'] }), error && jsxRuntime.jsxs("div", { style: { color: 'red' }, children: [jsxRuntime.jsx("strong", { children: "Error:" }), " ", error] }), xUser && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "user_id:" }), " ", xUser.userId, " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "clerk_user_id:" }), " ", xUser.clerkUserId || 'None', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "email:" }), " ", xUser.email || 'None', " ", jsxRuntime.jsx("br", {})] })), xCredit && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: "Credits:" }), " ", xCredit.balanceFree, " Free + ", xCredit.balancePaid, " Paid = ", xCredit.totalBalance, " Total"] })), jsxRuntime.jsx("div", { children: xSubscription ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("strong", { children: "user_id:" }), " ", xSubscription.userId, " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "pay_subscription_id:" }), " ", xSubscription.paySubscriptionId, " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "price_id:" }), " ", xSubscription.priceId || 'None', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "price_name:" }), " ", xSubscription.priceName || 'None', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "status:" }), " ", xSubscription.status || 'Free', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "credits_allocated:" }), " ", xSubscription.creditsAllocated || '', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "sub_period_start:" }), " ", xSubscription.subPeriodStart || '', " ", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { children: "sub_period_end:" }), " ", xSubscription.subPeriodEnd || '', " ", jsxRuntime.jsx("br", {})] })) : (jsxRuntime.jsx("strong", { children: "No Subscription, Default as Hobby Plan" })) })] })] }))] }));
|
|
95
115
|
}
|
|
96
116
|
|
|
97
117
|
exports.FingerprintProvider = FingerprintProvider;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { createContext, useContext, useState } from 'react';
|
|
3
|
+
import { createContext, useContext, useState, useRef, useEffect } from 'react';
|
|
4
4
|
import { useFingerprint } from './use-fingerprint.mjs';
|
|
5
|
+
import { globalLucideIcons } from '@windrun-huaiin/base-ui/components/server';
|
|
5
6
|
|
|
6
7
|
const FingerprintContext = createContext(undefined);
|
|
7
8
|
/**
|
|
@@ -45,9 +46,29 @@ function withFingerprint(Component, config) {
|
|
|
45
46
|
function FingerprintStatus() {
|
|
46
47
|
const { fingerprintId, xUser, xCredit, xSubscription, isLoading, isInitialized, error } = useFingerprintContext();
|
|
47
48
|
const [isOpen, setIsOpen] = useState(false);
|
|
49
|
+
const modalRef = useRef(null);
|
|
48
50
|
const handleToggle = () => {
|
|
49
51
|
setIsOpen(!isOpen);
|
|
50
52
|
};
|
|
53
|
+
const handleBackdropClick = (e) => {
|
|
54
|
+
if (e.target === e.currentTarget) {
|
|
55
|
+
setIsOpen(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
// ESC键关闭弹框
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
const handleEscKey = (e) => {
|
|
61
|
+
if (e.key === 'Escape' && isOpen) {
|
|
62
|
+
setIsOpen(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
if (isOpen) {
|
|
66
|
+
document.addEventListener('keydown', handleEscKey);
|
|
67
|
+
}
|
|
68
|
+
return () => {
|
|
69
|
+
document.removeEventListener('keydown', handleEscKey);
|
|
70
|
+
};
|
|
71
|
+
}, [isOpen]);
|
|
51
72
|
return (jsxs(Fragment, { children: [jsx("button", { onClick: handleToggle, style: {
|
|
52
73
|
position: 'fixed',
|
|
53
74
|
top: '10px',
|
|
@@ -63,12 +84,11 @@ function FingerprintStatus() {
|
|
|
63
84
|
alignItems: 'center',
|
|
64
85
|
justifyContent: 'center',
|
|
65
86
|
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
|
|
66
|
-
}, children: jsx(
|
|
67
|
-
fontSize: '24px',
|
|
87
|
+
}, children: jsx(globalLucideIcons.BTC, { size: 24, style: {
|
|
68
88
|
color: 'white',
|
|
69
89
|
transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
70
90
|
transition: 'transform 0.3s ease',
|
|
71
|
-
}
|
|
91
|
+
} }) }), isOpen && (jsxs(Fragment, { children: [jsx("div", { onClick: handleBackdropClick, style: {
|
|
72
92
|
position: 'fixed',
|
|
73
93
|
top: 0,
|
|
74
94
|
left: 0,
|
|
@@ -76,7 +96,7 @@ function FingerprintStatus() {
|
|
|
76
96
|
height: '100%',
|
|
77
97
|
background: 'rgba(0, 0, 0, 0.5)',
|
|
78
98
|
zIndex: 9998,
|
|
79
|
-
} }), jsxs("div", { style: {
|
|
99
|
+
} }), jsxs("div", { ref: modalRef, style: {
|
|
80
100
|
position: 'fixed',
|
|
81
101
|
top: '70px',
|
|
82
102
|
left: '10px',
|
|
@@ -85,11 +105,11 @@ function FingerprintStatus() {
|
|
|
85
105
|
borderRadius: '5px',
|
|
86
106
|
fontSize: '12px',
|
|
87
107
|
fontFamily: 'monospace',
|
|
88
|
-
maxWidth: '
|
|
108
|
+
maxWidth: '500px',
|
|
89
109
|
zIndex: 9999,
|
|
90
110
|
border: '1px solid #ccc',
|
|
91
111
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)',
|
|
92
|
-
}, children: [jsx("h4", { style: { margin: '0 0 5px 0' }, children: "Fingerprint Debug" }), jsxs("div", { children: [jsx("strong", { children: "FP_ID:" }), " ", fingerprintId || 'None'] }), jsxs("div", { children: [jsx("strong", { children: "Loading:" }), " ", isLoading ? 'Yes' : 'No'] }), jsxs("div", { children: [jsx("strong", { children: "Initialized:" }), " ", isInitialized ? 'Yes' : 'No'] }), error && jsxs("div", { style: { color: 'red' }, children: [jsx("strong", { children: "Error:" }), " ", error] }), xUser && (jsxs("div", { children: [jsx("strong", { children: "user_id:" }), " ", xUser.userId, " ", jsx("br", {}), jsx("strong", { children: "clerk_user_id:" }), " ", xUser.clerkUserId || 'None', " ", jsx("br", {}), jsx("strong", { children: "email:" }), " ", xUser.email || 'None', " ", jsx("br", {})] })), xCredit && (jsxs("div", { children: [jsx("strong", { children: "Credits:" }), " ", xCredit.balanceFree, " Free + ", xCredit.balancePaid, " Paid = ", xCredit.totalBalance, " Total"] })), xSubscription
|
|
112
|
+
}, children: [jsx("h4", { style: { margin: '0 0 5px 0' }, children: "Fingerprint Debug" }), jsxs("div", { children: [jsx("strong", { children: "FP_ID:" }), " ", fingerprintId || 'None'] }), jsxs("div", { children: [jsx("strong", { children: "Loading:" }), " ", isLoading ? 'Yes' : 'No'] }), jsxs("div", { children: [jsx("strong", { children: "Initialized:" }), " ", isInitialized ? 'Yes' : 'No'] }), error && jsxs("div", { style: { color: 'red' }, children: [jsx("strong", { children: "Error:" }), " ", error] }), xUser && (jsxs("div", { children: [jsx("strong", { children: "user_id:" }), " ", xUser.userId, " ", jsx("br", {}), jsx("strong", { children: "clerk_user_id:" }), " ", xUser.clerkUserId || 'None', " ", jsx("br", {}), jsx("strong", { children: "email:" }), " ", xUser.email || 'None', " ", jsx("br", {})] })), xCredit && (jsxs("div", { children: [jsx("strong", { children: "Credits:" }), " ", xCredit.balanceFree, " Free + ", xCredit.balancePaid, " Paid = ", xCredit.totalBalance, " Total"] })), jsx("div", { children: xSubscription ? (jsxs(Fragment, { children: [jsx("strong", { children: "user_id:" }), " ", xSubscription.userId, " ", jsx("br", {}), jsx("strong", { children: "pay_subscription_id:" }), " ", xSubscription.paySubscriptionId, " ", jsx("br", {}), jsx("strong", { children: "price_id:" }), " ", xSubscription.priceId || 'None', " ", jsx("br", {}), jsx("strong", { children: "price_name:" }), " ", xSubscription.priceName || 'None', " ", jsx("br", {}), jsx("strong", { children: "status:" }), " ", xSubscription.status || 'Free', " ", jsx("br", {}), jsx("strong", { children: "credits_allocated:" }), " ", xSubscription.creditsAllocated || '', " ", jsx("br", {}), jsx("strong", { children: "sub_period_start:" }), " ", xSubscription.subPeriodStart || '', " ", jsx("br", {}), jsx("strong", { children: "sub_period_end:" }), " ", xSubscription.subPeriodEnd || '', " ", jsx("br", {})] })) : (jsx("strong", { children: "No Subscription, Default as Hobby Plan" })) })] })] }))] }));
|
|
93
113
|
}
|
|
94
114
|
|
|
95
115
|
export { FingerprintProvider, FingerprintStatus, useFingerprintContext, useFingerprintContextSafe, withFingerprint };
|
package/dist/main/faq.js
CHANGED
|
@@ -23,7 +23,6 @@ require('fumadocs-core/link');
|
|
|
23
23
|
require('fumadocs-ui/components/ui/collapsible');
|
|
24
24
|
require('../fuma/mdx/banner.js');
|
|
25
25
|
require('./money-price/money-price-types.js');
|
|
26
|
-
require('./money-price/money-price-config.js');
|
|
27
26
|
|
|
28
27
|
function FAQ(_a) {
|
|
29
28
|
return tslib_es6.__awaiter(this, arguments, void 0, function* ({ locale, sectionClassName }) {
|
package/dist/main/faq.mjs
CHANGED
|
@@ -21,7 +21,6 @@ import 'fumadocs-core/link';
|
|
|
21
21
|
import 'fumadocs-ui/components/ui/collapsible';
|
|
22
22
|
import '../fuma/mdx/banner.mjs';
|
|
23
23
|
import './money-price/money-price-types.mjs';
|
|
24
|
-
import './money-price/money-price-config.mjs';
|
|
25
24
|
|
|
26
25
|
function FAQ(_a) {
|
|
27
26
|
return __awaiter(this, arguments, void 0, function* ({ locale, sectionClassName }) {
|
package/dist/main/gallery.js
CHANGED
|
@@ -23,7 +23,6 @@ require('fumadocs-core/link');
|
|
|
23
23
|
require('fumadocs-ui/components/ui/collapsible');
|
|
24
24
|
require('../fuma/mdx/banner.js');
|
|
25
25
|
require('./money-price/money-price-types.js');
|
|
26
|
-
require('./money-price/money-price-config.js');
|
|
27
26
|
|
|
28
27
|
function Gallery(_a) {
|
|
29
28
|
return tslib_es6.__awaiter(this, arguments, void 0, function* ({ locale, sectionClassName, button }) {
|
package/dist/main/gallery.mjs
CHANGED
|
@@ -21,7 +21,6 @@ import 'fumadocs-core/link';
|
|
|
21
21
|
import 'fumadocs-ui/components/ui/collapsible';
|
|
22
22
|
import '../fuma/mdx/banner.mjs';
|
|
23
23
|
import './money-price/money-price-types.mjs';
|
|
24
|
-
import './money-price/money-price-config.mjs';
|
|
25
24
|
|
|
26
25
|
function Gallery(_a) {
|
|
27
26
|
return __awaiter(this, arguments, void 0, function* ({ locale, sectionClassName, button }) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money Price Configuration
|
|
3
|
+
* 价格组件配置文件
|
|
4
|
+
*/
|
|
5
|
+
import type { MoneyPriceConfig, PaymentProviderConfig, EnhancePricePlan } from './money-price-types';
|
|
6
|
+
export declare function getActiveProviderConfig(config: MoneyPriceConfig): PaymentProviderConfig;
|
|
7
|
+
export declare function getProductPricing(productKey: 'free' | 'pro' | 'ultra', billingType: 'monthly' | 'yearly', provider: string, config: MoneyPriceConfig): EnhancePricePlan;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Money Price Configuration
|
|
5
|
+
* 价格组件配置文件
|
|
6
|
+
*/
|
|
7
|
+
// 辅助函数:获取当前激活的支付供应商配置
|
|
8
|
+
function getActiveProviderConfig(config) {
|
|
9
|
+
const provider = config.activeProvider;
|
|
10
|
+
return config.paymentProviders[provider];
|
|
11
|
+
}
|
|
12
|
+
// 辅助函数:获取特定产品的价格信息
|
|
13
|
+
function getProductPricing(productKey, billingType, provider, config) {
|
|
14
|
+
const providerConfig = config.paymentProviders[provider];
|
|
15
|
+
return providerConfig.products[productKey].plans[billingType];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.getActiveProviderConfig = getActiveProviderConfig;
|
|
19
|
+
exports.getProductPricing = getProductPricing;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money Price Configuration
|
|
3
|
+
* 价格组件配置文件
|
|
4
|
+
*/
|
|
5
|
+
// 辅助函数:获取当前激活的支付供应商配置
|
|
6
|
+
function getActiveProviderConfig(config) {
|
|
7
|
+
const provider = config.activeProvider;
|
|
8
|
+
return config.paymentProviders[provider];
|
|
9
|
+
}
|
|
10
|
+
// 辅助函数:获取特定产品的价格信息
|
|
11
|
+
function getProductPricing(productKey, billingType, provider, config) {
|
|
12
|
+
const providerConfig = config.paymentProviders[provider];
|
|
13
|
+
return providerConfig.products[productKey].plans[billingType];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { getActiveProviderConfig, getProductPricing };
|
|
@@ -10,7 +10,7 @@ var utils = require('@windrun-huaiin/lib/utils');
|
|
|
10
10
|
var navigation = require('next/navigation');
|
|
11
11
|
var client = require('react-dom/client');
|
|
12
12
|
var moneyPriceButton = require('./money-price-button.js');
|
|
13
|
-
var
|
|
13
|
+
var moneyPriceConfigUtil = require('./money-price-config-util.js');
|
|
14
14
|
var moneyPriceTypes = require('./money-price-types.js');
|
|
15
15
|
|
|
16
16
|
function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath }) {
|
|
@@ -62,7 +62,7 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
|
|
|
62
62
|
setIsProcessing(true);
|
|
63
63
|
try {
|
|
64
64
|
// 获取价格配置
|
|
65
|
-
const pricing =
|
|
65
|
+
const pricing = moneyPriceConfigUtil.getProductPricing(plan, billingType, config.activeProvider, config);
|
|
66
66
|
// 调用 API 创建支付会话
|
|
67
67
|
const response = yield fetch(upgradeApiEndpoint, {
|
|
68
68
|
method: 'POST',
|
|
@@ -94,7 +94,7 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
|
|
|
94
94
|
});
|
|
95
95
|
// 更新价格显示
|
|
96
96
|
const updatePriceDisplay = (newBillingType) => {
|
|
97
|
-
const providerConfig =
|
|
97
|
+
const providerConfig = moneyPriceConfigUtil.getActiveProviderConfig(config);
|
|
98
98
|
data.plans.forEach((plan) => {
|
|
99
99
|
const productConfig = providerConfig.products[plan.key];
|
|
100
100
|
const pricing = productConfig.plans[newBillingType];
|
|
@@ -167,7 +167,7 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
|
|
|
167
167
|
if (!discountInfoElement)
|
|
168
168
|
return;
|
|
169
169
|
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
170
|
-
const providerConfig =
|
|
170
|
+
const providerConfig = moneyPriceConfigUtil.getActiveProviderConfig(config);
|
|
171
171
|
// 检查是否有折扣
|
|
172
172
|
let hasDiscount = false;
|
|
173
173
|
let discountPercent = 0;
|
|
@@ -188,16 +188,38 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
|
|
|
188
188
|
discountInfoElement.appendChild(discountBadge);
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
|
+
// 使用 useRef 存储 root 实例,避免重复创建
|
|
192
|
+
const rootsRef = React.useRef(new Map());
|
|
191
193
|
// 动态替换按钮
|
|
192
194
|
React.useEffect(() => {
|
|
193
195
|
data.plans.forEach((plan) => {
|
|
194
196
|
const placeholder = document.querySelector(`[data-button-placeholder="${plan.key}"]`);
|
|
195
197
|
if (placeholder) {
|
|
196
|
-
|
|
198
|
+
let root = rootsRef.current.get(plan.key);
|
|
199
|
+
// 如果还没有创建 root,则创建一个
|
|
200
|
+
if (!root) {
|
|
201
|
+
root = client.createRoot(placeholder);
|
|
202
|
+
rootsRef.current.set(plan.key, root);
|
|
203
|
+
}
|
|
204
|
+
// 渲染按钮
|
|
197
205
|
root.render(jsxRuntime.jsx(moneyPriceButton.MoneyPriceButton, { planKey: plan.key, userContext: userContext, billingType: billingType, onLogin: handleLogin, onUpgrade: handleUpgrade, texts: data.buttonTexts, isProcessing: isProcessing }));
|
|
198
206
|
}
|
|
199
207
|
});
|
|
200
|
-
}, [userContext, billingType, isProcessing]);
|
|
208
|
+
}, [userContext, billingType, isProcessing, data.plans, data.buttonTexts, handleLogin, handleUpgrade]);
|
|
209
|
+
// 组件卸载时清理
|
|
210
|
+
React.useEffect(() => {
|
|
211
|
+
return () => {
|
|
212
|
+
rootsRef.current.forEach((root) => {
|
|
213
|
+
try {
|
|
214
|
+
root.unmount();
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
// 忽略卸载错误
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
rootsRef.current.clear();
|
|
221
|
+
};
|
|
222
|
+
}, []);
|
|
201
223
|
// 处理月付/年付切换和 tooltip 功能
|
|
202
224
|
React.useEffect(() => {
|
|
203
225
|
const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.mjs';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
import { useClerk } from '@clerk/nextjs';
|
|
5
|
-
import { useState, useEffect } from 'react';
|
|
5
|
+
import React, { useState, useEffect } from 'react';
|
|
6
6
|
import { useFingerprintContextSafe } from '../../clerk/fingerprint/fingerprint-provider.mjs';
|
|
7
7
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
8
8
|
import { useRouter } from 'next/navigation';
|
|
9
9
|
import { createRoot } from 'react-dom/client';
|
|
10
10
|
import { MoneyPriceButton } from './money-price-button.mjs';
|
|
11
|
-
import { getProductPricing, getActiveProviderConfig } from './money-price-config.mjs';
|
|
11
|
+
import { getProductPricing, getActiveProviderConfig } from './money-price-config-util.mjs';
|
|
12
12
|
import { UserState } from './money-price-types.mjs';
|
|
13
13
|
|
|
14
14
|
function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath }) {
|
|
@@ -186,16 +186,38 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
|
|
|
186
186
|
discountInfoElement.appendChild(discountBadge);
|
|
187
187
|
}
|
|
188
188
|
};
|
|
189
|
+
// 使用 useRef 存储 root 实例,避免重复创建
|
|
190
|
+
const rootsRef = React.useRef(new Map());
|
|
189
191
|
// 动态替换按钮
|
|
190
192
|
useEffect(() => {
|
|
191
193
|
data.plans.forEach((plan) => {
|
|
192
194
|
const placeholder = document.querySelector(`[data-button-placeholder="${plan.key}"]`);
|
|
193
195
|
if (placeholder) {
|
|
194
|
-
|
|
196
|
+
let root = rootsRef.current.get(plan.key);
|
|
197
|
+
// 如果还没有创建 root,则创建一个
|
|
198
|
+
if (!root) {
|
|
199
|
+
root = createRoot(placeholder);
|
|
200
|
+
rootsRef.current.set(plan.key, root);
|
|
201
|
+
}
|
|
202
|
+
// 渲染按钮
|
|
195
203
|
root.render(jsx(MoneyPriceButton, { planKey: plan.key, userContext: userContext, billingType: billingType, onLogin: handleLogin, onUpgrade: handleUpgrade, texts: data.buttonTexts, isProcessing: isProcessing }));
|
|
196
204
|
}
|
|
197
205
|
});
|
|
198
|
-
}, [userContext, billingType, isProcessing]);
|
|
206
|
+
}, [userContext, billingType, isProcessing, data.plans, data.buttonTexts, handleLogin, handleUpgrade]);
|
|
207
|
+
// 组件卸载时清理
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
return () => {
|
|
210
|
+
rootsRef.current.forEach((root) => {
|
|
211
|
+
try {
|
|
212
|
+
root.unmount();
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
// 忽略卸载错误
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
rootsRef.current.clear();
|
|
219
|
+
};
|
|
220
|
+
}, []);
|
|
199
221
|
// 处理月付/年付切换和 tooltip 功能
|
|
200
222
|
useEffect(() => {
|
|
201
223
|
const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
|
|
@@ -14,7 +14,7 @@ export interface UserContext {
|
|
|
14
14
|
subscriptionType?: 'monthly' | 'yearly';
|
|
15
15
|
subscriptionEndDate?: string;
|
|
16
16
|
}
|
|
17
|
-
export type PaymentProvider = 'stripe' | '
|
|
17
|
+
export type PaymentProvider = 'stripe' | 'apple' | 'paypal' | 'wechat' | 'alipay';
|
|
18
18
|
export interface EnhancePricePlan {
|
|
19
19
|
priceId: string;
|
|
20
20
|
amount: number;
|
|
@@ -16,7 +16,7 @@ require('@windrun-huaiin/base-ui/ui');
|
|
|
16
16
|
require('fumadocs-ui/components/ui/collapsible');
|
|
17
17
|
require('../../fuma/mdx/banner.js');
|
|
18
18
|
var server = require('next-intl/server');
|
|
19
|
-
var
|
|
19
|
+
var moneyPriceConfigUtil = require('./money-price-config-util.js');
|
|
20
20
|
var moneyPriceInteractive = require('./money-price-interactive.js');
|
|
21
21
|
|
|
22
22
|
function MoneyPrice(_a) {
|
|
@@ -32,7 +32,7 @@ function MoneyPrice(_a) {
|
|
|
32
32
|
currency: config.display.currency
|
|
33
33
|
};
|
|
34
34
|
// 获取激活的支付供应商配置
|
|
35
|
-
const providerConfig =
|
|
35
|
+
const providerConfig = moneyPriceConfigUtil.getActiveProviderConfig(config);
|
|
36
36
|
const minPlanFeaturesCount = config.display.minFeaturesCount;
|
|
37
37
|
// 使用默认计费类型进行静态渲染
|
|
38
38
|
const defaultBilling = data.billingSwitch.defaultKey;
|
|
@@ -14,7 +14,7 @@ import '@windrun-huaiin/base-ui/ui';
|
|
|
14
14
|
import 'fumadocs-ui/components/ui/collapsible';
|
|
15
15
|
import '../../fuma/mdx/banner.mjs';
|
|
16
16
|
import { getTranslations } from 'next-intl/server';
|
|
17
|
-
import { getActiveProviderConfig } from './money-price-config.mjs';
|
|
17
|
+
import { getActiveProviderConfig } from './money-price-config-util.mjs';
|
|
18
18
|
import { MoneyPriceInteractive } from './money-price-interactive.mjs';
|
|
19
19
|
|
|
20
20
|
function MoneyPrice(_a) {
|
package/dist/main/price-plan.js
CHANGED
|
@@ -23,7 +23,6 @@ require('fumadocs-core/link');
|
|
|
23
23
|
require('fumadocs-ui/components/ui/collapsible');
|
|
24
24
|
require('../fuma/mdx/banner.js');
|
|
25
25
|
require('./money-price/money-price-types.js');
|
|
26
|
-
require('./money-price/money-price-config.js');
|
|
27
26
|
|
|
28
27
|
function PricePlan(_a) {
|
|
29
28
|
return tslib_es6.__awaiter(this, arguments, void 0, function* ({ locale, currency = '$', pricePlanConfig, sectionClassName }) {
|
package/dist/main/price-plan.mjs
CHANGED
|
@@ -21,7 +21,6 @@ import 'fumadocs-core/link';
|
|
|
21
21
|
import 'fumadocs-ui/components/ui/collapsible';
|
|
22
22
|
import '../fuma/mdx/banner.mjs';
|
|
23
23
|
import './money-price/money-price-types.mjs';
|
|
24
|
-
import './money-price/money-price-config.mjs';
|
|
25
24
|
|
|
26
25
|
function PricePlan(_a) {
|
|
27
26
|
return __awaiter(this, arguments, void 0, function* ({ locale, currency = '$', pricePlanConfig, sectionClassName }) {
|
package/dist/main/server.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export * from './cta';
|
|
|
8
8
|
export * from './footer';
|
|
9
9
|
export * from './price-plan';
|
|
10
10
|
export { MoneyPrice } from './money-price/money-price';
|
|
11
|
-
export {
|
|
11
|
+
export { getActiveProviderConfig, getProductPricing } from './money-price/money-price-config-util';
|
|
12
12
|
export type { MoneyPriceConfig, MoneyPriceProps, MoneyPriceInteractiveProps, MoneyPriceButtonProps, MoneyPriceData, PaymentProvider, PaymentProviderConfig, EnhancePricePlan, ProductConfig, UserContext } from './money-price/money-price-types';
|
|
13
13
|
export { UserState } from './money-price/money-price-types';
|
package/dist/main/server.js
CHANGED
|
@@ -10,7 +10,7 @@ var cta = require('./cta.js');
|
|
|
10
10
|
var footer = require('./footer.js');
|
|
11
11
|
var pricePlan = require('./price-plan.js');
|
|
12
12
|
var moneyPrice = require('./money-price/money-price.js');
|
|
13
|
-
var
|
|
13
|
+
var moneyPriceConfigUtil = require('./money-price/money-price-config-util.js');
|
|
14
14
|
var moneyPriceTypes = require('./money-price/money-price-types.js');
|
|
15
15
|
|
|
16
16
|
|
|
@@ -25,9 +25,8 @@ exports.CTA = cta.CTA;
|
|
|
25
25
|
exports.Footer = footer.Footer;
|
|
26
26
|
exports.PricePlan = pricePlan.PricePlan;
|
|
27
27
|
exports.MoneyPrice = moneyPrice.MoneyPrice;
|
|
28
|
-
exports.getActiveProviderConfig =
|
|
29
|
-
exports.getProductPricing =
|
|
30
|
-
exports.moneyPriceConfig = moneyPriceConfig.moneyPriceConfig;
|
|
28
|
+
exports.getActiveProviderConfig = moneyPriceConfigUtil.getActiveProviderConfig;
|
|
29
|
+
exports.getProductPricing = moneyPriceConfigUtil.getProductPricing;
|
|
31
30
|
Object.defineProperty(exports, "UserState", {
|
|
32
31
|
enumerable: true,
|
|
33
32
|
get: function () { return moneyPriceTypes.UserState; }
|
package/dist/main/server.mjs
CHANGED
|
@@ -8,5 +8,5 @@ export { CTA } from './cta.mjs';
|
|
|
8
8
|
export { Footer } from './footer.mjs';
|
|
9
9
|
export { PricePlan } from './price-plan.mjs';
|
|
10
10
|
export { MoneyPrice } from './money-price/money-price.mjs';
|
|
11
|
-
export { getActiveProviderConfig, getProductPricing
|
|
11
|
+
export { getActiveProviderConfig, getProductPricing } from './money-price/money-price-config-util.mjs';
|
|
12
12
|
export { UserState } from './money-price/money-price-types.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as coseBase$1 } from '../../../../../_virtual/cose-
|
|
1
|
+
import { __module as coseBase$1 } from '../../../../../_virtual/cose-base2.mjs';
|
|
2
2
|
import { __require as requireLayoutBase } from '../../../layout-base@1.0.2/node_modules/layout-base/layout-base.mjs';
|
|
3
3
|
|
|
4
4
|
var coseBase = coseBase$1.exports;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as coseBase$1 } from '../../../../../_virtual/cose-
|
|
1
|
+
import { __module as coseBase$1 } from '../../../../../_virtual/cose-base.mjs';
|
|
2
2
|
import { __require as requireLayoutBase } from '../../../layout-base@2.0.1/node_modules/layout-base/layout-base.mjs';
|
|
3
3
|
|
|
4
4
|
var coseBase = coseBase$1.exports;
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext, useContext, useState } from 'react';
|
|
3
|
+
import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
|
|
4
4
|
import { useFingerprint } from './use-fingerprint';
|
|
5
|
+
import { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/server';
|
|
5
6
|
import type {
|
|
6
7
|
FingerprintContextType,
|
|
7
8
|
FingerprintProviderProps
|
|
@@ -78,11 +79,35 @@ export function FingerprintStatus() {
|
|
|
78
79
|
} = useFingerprintContext();
|
|
79
80
|
|
|
80
81
|
const [isOpen, setIsOpen] = useState(false);
|
|
82
|
+
const modalRef = useRef<HTMLDivElement>(null);
|
|
81
83
|
|
|
82
84
|
const handleToggle = () => {
|
|
83
85
|
setIsOpen(!isOpen);
|
|
84
86
|
};
|
|
85
87
|
|
|
88
|
+
const handleBackdropClick = (e: React.MouseEvent) => {
|
|
89
|
+
if (e.target === e.currentTarget) {
|
|
90
|
+
setIsOpen(false);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ESC键关闭弹框
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const handleEscKey = (e: KeyboardEvent) => {
|
|
97
|
+
if (e.key === 'Escape' && isOpen) {
|
|
98
|
+
setIsOpen(false);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (isOpen) {
|
|
103
|
+
document.addEventListener('keydown', handleEscKey);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
document.removeEventListener('keydown', handleEscKey);
|
|
108
|
+
};
|
|
109
|
+
}, [isOpen]);
|
|
110
|
+
|
|
86
111
|
return (
|
|
87
112
|
<>
|
|
88
113
|
<button
|
|
@@ -104,17 +129,20 @@ export function FingerprintStatus() {
|
|
|
104
129
|
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
|
|
105
130
|
}}
|
|
106
131
|
>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
<icons.BTC
|
|
133
|
+
size={24}
|
|
134
|
+
style={{
|
|
135
|
+
color: 'white',
|
|
136
|
+
transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
137
|
+
transition: 'transform 0.3s ease',
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
113
140
|
</button>
|
|
114
141
|
|
|
115
142
|
{isOpen && (
|
|
116
143
|
<>
|
|
117
144
|
<div
|
|
145
|
+
onClick={handleBackdropClick}
|
|
118
146
|
style={{
|
|
119
147
|
position: 'fixed',
|
|
120
148
|
top: 0,
|
|
@@ -126,6 +154,7 @@ export function FingerprintStatus() {
|
|
|
126
154
|
}}
|
|
127
155
|
/>
|
|
128
156
|
<div
|
|
157
|
+
ref={modalRef}
|
|
129
158
|
style={{
|
|
130
159
|
position: 'fixed',
|
|
131
160
|
top: '70px',
|
|
@@ -135,7 +164,7 @@ export function FingerprintStatus() {
|
|
|
135
164
|
borderRadius: '5px',
|
|
136
165
|
fontSize: '12px',
|
|
137
166
|
fontFamily: 'monospace',
|
|
138
|
-
maxWidth: '
|
|
167
|
+
maxWidth: '500px',
|
|
139
168
|
zIndex: 9999,
|
|
140
169
|
border: '1px solid #ccc',
|
|
141
170
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)',
|
|
@@ -158,18 +187,22 @@ export function FingerprintStatus() {
|
|
|
158
187
|
<strong>Credits:</strong> {xCredit.balanceFree} Free + {xCredit.balancePaid} Paid = {xCredit.totalBalance} Total
|
|
159
188
|
</div>
|
|
160
189
|
)}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
<div>
|
|
191
|
+
{xSubscription ? (
|
|
192
|
+
<>
|
|
193
|
+
<strong>user_id:</strong> {xSubscription.userId} <br/>
|
|
194
|
+
<strong>pay_subscription_id:</strong> {xSubscription.paySubscriptionId} <br/>
|
|
195
|
+
<strong>price_id:</strong> {xSubscription.priceId || 'None'} <br/>
|
|
196
|
+
<strong>price_name:</strong> {xSubscription.priceName || 'None'} <br/>
|
|
197
|
+
<strong>status:</strong> {xSubscription.status || 'Free'} <br/>
|
|
198
|
+
<strong>credits_allocated:</strong> {xSubscription.creditsAllocated || ''} <br/>
|
|
199
|
+
<strong>sub_period_start:</strong> {xSubscription.subPeriodStart || ''} <br/>
|
|
200
|
+
<strong>sub_period_end:</strong> {xSubscription.subPeriodEnd || ''} <br/>
|
|
201
|
+
</>
|
|
202
|
+
) : (
|
|
203
|
+
<strong>No Subscription, Default as Hobby Plan</strong>
|
|
204
|
+
)}
|
|
205
|
+
</div>
|
|
173
206
|
</div>
|
|
174
207
|
</>
|
|
175
208
|
)}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money Price Configuration
|
|
3
|
+
* 价格组件配置文件
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { MoneyPriceConfig, PaymentProviderConfig, EnhancePricePlan } from './money-price-types';
|
|
7
|
+
|
|
8
|
+
// 辅助函数:获取当前激活的支付供应商配置
|
|
9
|
+
export function getActiveProviderConfig(config: MoneyPriceConfig): PaymentProviderConfig {
|
|
10
|
+
const provider = config.activeProvider;
|
|
11
|
+
return config.paymentProviders[provider];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 辅助函数:获取特定产品的价格信息
|
|
15
|
+
export function getProductPricing(
|
|
16
|
+
productKey: 'free' | 'pro' | 'ultra',
|
|
17
|
+
billingType: 'monthly' | 'yearly',
|
|
18
|
+
provider: string,
|
|
19
|
+
config: MoneyPriceConfig
|
|
20
|
+
): EnhancePricePlan {
|
|
21
|
+
const providerConfig = config.paymentProviders[provider];
|
|
22
|
+
return providerConfig.products[productKey].plans[billingType];
|
|
23
|
+
}
|
|
@@ -7,7 +7,7 @@ import { useRouter } from 'next/navigation';
|
|
|
7
7
|
import React, { useEffect, useState } from 'react';
|
|
8
8
|
import { createRoot } from 'react-dom/client';
|
|
9
9
|
import { MoneyPriceButton } from './money-price-button';
|
|
10
|
-
import { getActiveProviderConfig, getProductPricing } from './money-price-config';
|
|
10
|
+
import { getActiveProviderConfig, getProductPricing } from './money-price-config-util';
|
|
11
11
|
import {
|
|
12
12
|
UserState,
|
|
13
13
|
type MoneyPriceInteractiveProps,
|
|
@@ -218,12 +218,23 @@ export function MoneyPriceInteractive({
|
|
|
218
218
|
}
|
|
219
219
|
};
|
|
220
220
|
|
|
221
|
+
// 使用 useRef 存储 root 实例,避免重复创建
|
|
222
|
+
const rootsRef = React.useRef<Map<string, any>>(new Map());
|
|
223
|
+
|
|
221
224
|
// 动态替换按钮
|
|
222
225
|
useEffect(() => {
|
|
223
226
|
data.plans.forEach((plan: any) => {
|
|
224
227
|
const placeholder = document.querySelector(`[data-button-placeholder="${plan.key}"]`);
|
|
225
228
|
if (placeholder) {
|
|
226
|
-
|
|
229
|
+
let root = rootsRef.current.get(plan.key);
|
|
230
|
+
|
|
231
|
+
// 如果还没有创建 root,则创建一个
|
|
232
|
+
if (!root) {
|
|
233
|
+
root = createRoot(placeholder);
|
|
234
|
+
rootsRef.current.set(plan.key, root);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 渲染按钮
|
|
227
238
|
root.render(
|
|
228
239
|
<MoneyPriceButton
|
|
229
240
|
planKey={plan.key}
|
|
@@ -237,7 +248,21 @@ export function MoneyPriceInteractive({
|
|
|
237
248
|
);
|
|
238
249
|
}
|
|
239
250
|
});
|
|
240
|
-
}, [userContext, billingType, isProcessing]);
|
|
251
|
+
}, [userContext, billingType, isProcessing, data.plans, data.buttonTexts, handleLogin, handleUpgrade]);
|
|
252
|
+
|
|
253
|
+
// 组件卸载时清理
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
return () => {
|
|
256
|
+
rootsRef.current.forEach((root) => {
|
|
257
|
+
try {
|
|
258
|
+
root.unmount();
|
|
259
|
+
} catch (e) {
|
|
260
|
+
// 忽略卸载错误
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
rootsRef.current.clear();
|
|
264
|
+
};
|
|
265
|
+
}, []);
|
|
241
266
|
|
|
242
267
|
// 处理月付/年付切换和 tooltip 功能
|
|
243
268
|
useEffect(() => {
|
|
@@ -20,7 +20,7 @@ export interface UserContext {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// 支付供应商类型
|
|
23
|
-
export type PaymentProvider = 'stripe' | '
|
|
23
|
+
export type PaymentProvider = 'stripe' | 'apple' | 'paypal' | 'wechat' | 'alipay' ;
|
|
24
24
|
|
|
25
25
|
// 价格计划
|
|
26
26
|
export interface EnhancePricePlan {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { GradientButton } from '@third-ui/fuma/mdx';
|
|
3
3
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
4
|
import { getTranslations } from 'next-intl/server';
|
|
5
|
-
import { getActiveProviderConfig } from './money-price-config';
|
|
5
|
+
import { getActiveProviderConfig } from './money-price-config-util';
|
|
6
6
|
import { MoneyPriceInteractive } from './money-price-interactive';
|
|
7
7
|
import type { MoneyPriceData, MoneyPriceProps } from './money-price-types';
|
|
8
8
|
|
package/src/main/server.ts
CHANGED
|
@@ -12,10 +12,9 @@ export * from './price-plan';
|
|
|
12
12
|
// Money Price Server Component and Types
|
|
13
13
|
export { MoneyPrice } from './money-price/money-price';
|
|
14
14
|
export {
|
|
15
|
-
moneyPriceConfig,
|
|
16
15
|
getActiveProviderConfig,
|
|
17
16
|
getProductPricing
|
|
18
|
-
} from './money-price/money-price-config';
|
|
17
|
+
} from './money-price/money-price-config-util';
|
|
19
18
|
|
|
20
19
|
// Money Price Types (shared between server and client)
|
|
21
20
|
export type {
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Money Price Configuration
|
|
3
|
-
* 价格组件配置文件
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { MoneyPriceConfig, PaymentProviderConfig, EnhancePricePlan } from './money-price-types';
|
|
7
|
-
|
|
8
|
-
// 示例配置
|
|
9
|
-
export const moneyPriceConfig: MoneyPriceConfig = {
|
|
10
|
-
paymentProviders: {
|
|
11
|
-
stripe: {
|
|
12
|
-
provider: 'stripe',
|
|
13
|
-
enabled: true,
|
|
14
|
-
products: {
|
|
15
|
-
free: {
|
|
16
|
-
key: 'free',
|
|
17
|
-
name: 'free', // Just a key, actual display name comes from translation
|
|
18
|
-
plans: {
|
|
19
|
-
monthly: {
|
|
20
|
-
priceId: 'free',
|
|
21
|
-
amount: 0,
|
|
22
|
-
currency: 'usd',
|
|
23
|
-
credits: 0
|
|
24
|
-
},
|
|
25
|
-
yearly: {
|
|
26
|
-
priceId: 'free',
|
|
27
|
-
amount: 0,
|
|
28
|
-
currency: 'usd',
|
|
29
|
-
credits: 0
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
pro: {
|
|
34
|
-
key: 'pro',
|
|
35
|
-
name: 'pro', // Just a key, actual display name comes from translation
|
|
36
|
-
plans: {
|
|
37
|
-
monthly: {
|
|
38
|
-
priceId: process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID || 'price_pro_monthly',
|
|
39
|
-
amount: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_AMOUNT) || 10,
|
|
40
|
-
currency: process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_CURRENCY || 'usd',
|
|
41
|
-
credits: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_CREDITS) || 100
|
|
42
|
-
},
|
|
43
|
-
yearly: {
|
|
44
|
-
priceId: process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_PRICE_ID || 'price_pro_yearly',
|
|
45
|
-
amount: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_AMOUNT) || 96,
|
|
46
|
-
originalAmount: 120,
|
|
47
|
-
discountPercent: 20,
|
|
48
|
-
currency: process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_CURRENCY || 'usd',
|
|
49
|
-
credits: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_CREDITS) || 1200
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
ultra: {
|
|
54
|
-
key: 'ultra',
|
|
55
|
-
name: 'ultra', // Just a key, actual display name comes from translation
|
|
56
|
-
plans: {
|
|
57
|
-
monthly: {
|
|
58
|
-
priceId: process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_PRICE_ID || 'price_ultra_monthly',
|
|
59
|
-
amount: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_AMOUNT) || 20,
|
|
60
|
-
currency: process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_CURRENCY || 'usd',
|
|
61
|
-
credits: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_CREDITS) || 250
|
|
62
|
-
},
|
|
63
|
-
yearly: {
|
|
64
|
-
priceId: process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_PRICE_ID || 'price_ultra_yearly',
|
|
65
|
-
amount: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_AMOUNT) || 192,
|
|
66
|
-
originalAmount: 240,
|
|
67
|
-
discountPercent: 20,
|
|
68
|
-
currency: process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_CURRENCY || 'usd',
|
|
69
|
-
credits: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_CREDITS) || 3000
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
alipay: {
|
|
76
|
-
provider: 'alipay',
|
|
77
|
-
enabled: false,
|
|
78
|
-
products: {
|
|
79
|
-
free: {
|
|
80
|
-
key: 'free',
|
|
81
|
-
name: 'free',
|
|
82
|
-
plans: {
|
|
83
|
-
monthly: {
|
|
84
|
-
priceId: 'free',
|
|
85
|
-
amount: 0,
|
|
86
|
-
currency: 'cny',
|
|
87
|
-
credits: 0
|
|
88
|
-
},
|
|
89
|
-
yearly: {
|
|
90
|
-
priceId: 'free',
|
|
91
|
-
amount: 0,
|
|
92
|
-
currency: 'cny',
|
|
93
|
-
credits: 0
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
pro: {
|
|
98
|
-
key: 'pro',
|
|
99
|
-
name: 'pro',
|
|
100
|
-
plans: {
|
|
101
|
-
monthly: {
|
|
102
|
-
priceId: 'alipay_pro_monthly',
|
|
103
|
-
amount: 70,
|
|
104
|
-
currency: 'cny',
|
|
105
|
-
credits: 100
|
|
106
|
-
},
|
|
107
|
-
yearly: {
|
|
108
|
-
priceId: 'alipay_pro_yearly',
|
|
109
|
-
amount: 672,
|
|
110
|
-
originalAmount: 840,
|
|
111
|
-
discountPercent: 20,
|
|
112
|
-
currency: 'cny',
|
|
113
|
-
credits: 1200
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
ultra: {
|
|
118
|
-
key: 'ultra',
|
|
119
|
-
name: 'ultra',
|
|
120
|
-
plans: {
|
|
121
|
-
monthly: {
|
|
122
|
-
priceId: 'alipay_ultra_monthly',
|
|
123
|
-
amount: 140,
|
|
124
|
-
currency: 'cny',
|
|
125
|
-
credits: 250
|
|
126
|
-
},
|
|
127
|
-
yearly: {
|
|
128
|
-
priceId: 'alipay_ultra_yearly',
|
|
129
|
-
amount: 1344,
|
|
130
|
-
originalAmount: 1680,
|
|
131
|
-
discountPercent: 20,
|
|
132
|
-
currency: 'cny',
|
|
133
|
-
credits: 3000
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
wechat: {
|
|
140
|
-
provider: 'wechat',
|
|
141
|
-
enabled: false,
|
|
142
|
-
products: {
|
|
143
|
-
free: {
|
|
144
|
-
key: 'free',
|
|
145
|
-
name: 'free',
|
|
146
|
-
plans: {
|
|
147
|
-
monthly: {
|
|
148
|
-
priceId: 'free',
|
|
149
|
-
amount: 0,
|
|
150
|
-
currency: 'cny',
|
|
151
|
-
credits: 0
|
|
152
|
-
},
|
|
153
|
-
yearly: {
|
|
154
|
-
priceId: 'free',
|
|
155
|
-
amount: 0,
|
|
156
|
-
currency: 'cny',
|
|
157
|
-
credits: 0
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
pro: {
|
|
162
|
-
key: 'pro',
|
|
163
|
-
name: 'pro',
|
|
164
|
-
plans: {
|
|
165
|
-
monthly: {
|
|
166
|
-
priceId: 'wechat_pro_monthly',
|
|
167
|
-
amount: 70,
|
|
168
|
-
currency: 'cny',
|
|
169
|
-
credits: 100
|
|
170
|
-
},
|
|
171
|
-
yearly: {
|
|
172
|
-
priceId: 'wechat_pro_yearly',
|
|
173
|
-
amount: 672,
|
|
174
|
-
originalAmount: 840,
|
|
175
|
-
discountPercent: 20,
|
|
176
|
-
currency: 'cny',
|
|
177
|
-
credits: 1200
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
ultra: {
|
|
182
|
-
key: 'ultra',
|
|
183
|
-
name: 'ultra',
|
|
184
|
-
plans: {
|
|
185
|
-
monthly: {
|
|
186
|
-
priceId: 'wechat_ultra_monthly',
|
|
187
|
-
amount: 140,
|
|
188
|
-
currency: 'cny',
|
|
189
|
-
credits: 250
|
|
190
|
-
},
|
|
191
|
-
yearly: {
|
|
192
|
-
priceId: 'wechat_ultra_yearly',
|
|
193
|
-
amount: 1344,
|
|
194
|
-
originalAmount: 1680,
|
|
195
|
-
discountPercent: 20,
|
|
196
|
-
currency: 'cny',
|
|
197
|
-
credits: 3000
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
|
|
205
|
-
activeProvider: process.env.NEXT_PUBLIC_ACTIVE_PAYMENT_PROVIDER || 'stripe',
|
|
206
|
-
|
|
207
|
-
display: {
|
|
208
|
-
currency: '$',
|
|
209
|
-
locale: 'en',
|
|
210
|
-
minFeaturesCount: 4
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// 辅助函数:获取当前激活的支付供应商配置
|
|
215
|
-
export function getActiveProviderConfig(config: MoneyPriceConfig): PaymentProviderConfig {
|
|
216
|
-
const provider = config.activeProvider;
|
|
217
|
-
return config.paymentProviders[provider];
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// 辅助函数:获取特定产品的价格信息
|
|
221
|
-
export function getProductPricing(
|
|
222
|
-
productKey: 'free' | 'pro' | 'ultra',
|
|
223
|
-
billingType: 'monthly' | 'yearly',
|
|
224
|
-
provider: string,
|
|
225
|
-
config: MoneyPriceConfig
|
|
226
|
-
): EnhancePricePlan {
|
|
227
|
-
const providerConfig = config.paymentProviders[provider];
|
|
228
|
-
return providerConfig.products[productKey].plans[billingType];
|
|
229
|
-
}
|