mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-18 10:27:28 +00:00
💸 chore: Align subscription pricing display with global currency settings
Unify subscription price rendering to use the site-wide currency symbol/rate on the wallet and admin views. Make subscription plan currency read-only in the editor and force USD on create/update to avoid drift. Use global currency display type when creating Creem checkout payloads.
This commit is contained in:
@@ -19,6 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Modal, Space, Tag } from '@douyinfe/semi-ui';
|
||||
import { convertUSDToCurrency } from '../../helpers/render';
|
||||
|
||||
const quotaTypeLabel = (quotaType) => (quotaType === 1 ? '按次' : '按量');
|
||||
|
||||
@@ -48,8 +49,8 @@ const renderPlanTitle = (text, record) => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderPrice = (text, record) => {
|
||||
return `${record?.plan?.currency || 'USD'} ${Number(text || 0).toFixed(2)}`;
|
||||
const renderPrice = (text) => {
|
||||
return convertUSDToCurrency(Number(text || 0), 2);
|
||||
};
|
||||
|
||||
const renderDuration = (text, record, t) => {
|
||||
|
||||
@@ -114,7 +114,7 @@ const AddEditSubscriptionModal = ({
|
||||
title: p.title || '',
|
||||
subtitle: p.subtitle || '',
|
||||
price_amount: Number(p.price_amount || 0),
|
||||
currency: p.currency || 'USD',
|
||||
currency: 'USD',
|
||||
duration_unit: p.duration_unit || 'month',
|
||||
duration_value: Number(p.duration_value || 1),
|
||||
custom_seconds: Number(p.custom_seconds || 0),
|
||||
@@ -274,6 +274,7 @@ const AddEditSubscriptionModal = ({
|
||||
plan: {
|
||||
...values,
|
||||
price_amount: Number(values.price_amount || 0),
|
||||
currency: 'USD',
|
||||
duration_value: Number(values.duration_value || 0),
|
||||
custom_seconds: Number(values.custom_seconds || 0),
|
||||
quota_reset_period: values.quota_reset_period || 'never',
|
||||
@@ -467,15 +468,12 @@ const AddEditSubscriptionModal = ({
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Select
|
||||
<Form.Input
|
||||
field='currency'
|
||||
label={t('币种')}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select.Option value='USD'>USD</Select.Option>
|
||||
<Select.Option value='EUR'>EUR</Select.Option>
|
||||
<Select.Option value='CNY'>CNY</Select.Option>
|
||||
</Form.Select>
|
||||
disabled
|
||||
extraText={t('由全站货币展示设置统一控制')}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
IllustrationNoResultDark,
|
||||
} from '@douyinfe/semi-illustrations';
|
||||
import { API, showError, showSuccess } from '../../../../helpers';
|
||||
import { convertUSDToCurrency } from '../../../../helpers/render';
|
||||
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
|
||||
import CardTable from '../../../common/ui/CardTable';
|
||||
|
||||
@@ -104,7 +105,10 @@ const UserSubscriptionsModal = ({ visible, onCancel, user, t, onSuccess }) => {
|
||||
|
||||
const planOptions = useMemo(() => {
|
||||
return (plans || []).map((p) => ({
|
||||
label: `${p?.plan?.title || ''} (${p?.plan?.currency || 'USD'} ${Number(p?.plan?.price_amount || 0)})`,
|
||||
label: `${p?.plan?.title || ''} (${convertUSDToCurrency(
|
||||
Number(p?.plan?.price_amount || 0),
|
||||
2,
|
||||
)})`,
|
||||
value: p?.plan?.id,
|
||||
}));
|
||||
}, [plans]);
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
Typography,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import { API, showError, showSuccess } from '../../helpers';
|
||||
import { getCurrencyConfig } from '../../helpers/render';
|
||||
import { CalendarClock, Check, Crown, RefreshCw, Sparkles } from 'lucide-react';
|
||||
import SubscriptionPurchaseModal from './modals/SubscriptionPurchaseModal';
|
||||
|
||||
@@ -99,12 +100,6 @@ function submitEpayForm({ url, params }) {
|
||||
document.body.removeChild(form);
|
||||
}
|
||||
|
||||
// 获取货币符号
|
||||
function getCurrencySymbol(currency) {
|
||||
const symbols = { USD: '$', EUR: '€', CNY: '¥', GBP: '£', JPY: '¥' };
|
||||
return symbols[currency] || currency + ' ';
|
||||
}
|
||||
|
||||
const SubscriptionPlansCard = ({
|
||||
t,
|
||||
loading = false,
|
||||
@@ -457,8 +452,11 @@ const SubscriptionPlansCard = ({
|
||||
{plans.map((p, index) => {
|
||||
const plan = p?.plan;
|
||||
const planItems = p?.items || [];
|
||||
const currency = getCurrencySymbol(plan?.currency || 'USD');
|
||||
const { symbol, rate } = getCurrencyConfig();
|
||||
const price = Number(plan?.price_amount || 0);
|
||||
const displayPrice = (price * rate).toFixed(
|
||||
price % 1 === 0 ? 0 : 2,
|
||||
);
|
||||
const isPopular = index === 0 && plans.length > 1;
|
||||
|
||||
return (
|
||||
@@ -504,10 +502,10 @@ const SubscriptionPlansCard = ({
|
||||
<div className='text-center py-2'>
|
||||
<div className='flex items-baseline justify-center'>
|
||||
<span className='text-xl font-bold text-purple-600'>
|
||||
{currency}
|
||||
{symbol}
|
||||
</span>
|
||||
<span className='text-3xl font-bold text-purple-600'>
|
||||
{price.toFixed(price % 1 === 0 ? 0 : 2)}
|
||||
{displayPrice}
|
||||
</span>
|
||||
</div>
|
||||
<div className='text-sm text-gray-500 mt-1'>
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
import { Crown, CalendarClock, Package, Check } from 'lucide-react';
|
||||
import { SiStripe } from 'react-icons/si';
|
||||
import { IconCreditCard } from '@douyinfe/semi-icons';
|
||||
import { getCurrencyConfig } from '../../../helpers/render';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
@@ -70,12 +71,6 @@ function formatResetPeriod(plan, t) {
|
||||
return t('不重置');
|
||||
}
|
||||
|
||||
// 获取货币符号
|
||||
function getCurrencySymbol(currency) {
|
||||
const symbols = { USD: '$', EUR: '€', CNY: '¥', GBP: '£', JPY: '¥' };
|
||||
return symbols[currency] || currency + ' ';
|
||||
}
|
||||
|
||||
const SubscriptionPurchaseModal = ({
|
||||
t,
|
||||
visible,
|
||||
@@ -94,8 +89,9 @@ const SubscriptionPurchaseModal = ({
|
||||
}) => {
|
||||
const plan = selectedPlan?.plan;
|
||||
const items = selectedPlan?.items || [];
|
||||
const currency = plan ? getCurrencySymbol(plan.currency || 'USD') : '$';
|
||||
const { symbol, rate } = getCurrencyConfig();
|
||||
const price = plan ? Number(plan.price_amount || 0) : 0;
|
||||
const displayPrice = (price * rate).toFixed(price % 1 === 0 ? 0 : 2);
|
||||
// 只有当管理员开启支付网关 AND 套餐配置了对应的支付ID时才显示
|
||||
const hasStripe = enableStripeTopUp && !!plan?.stripe_price_id;
|
||||
const hasCreem = enableCreemTopUp && !!plan?.creem_product_id;
|
||||
@@ -170,8 +166,8 @@ const SubscriptionPurchaseModal = ({
|
||||
{t('应付金额')}:
|
||||
</Text>
|
||||
<Text strong className='text-xl text-purple-600'>
|
||||
{currency}
|
||||
{price.toFixed(price % 1 === 0 ? 0 : 2)}
|
||||
{symbol}
|
||||
{displayPrice}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user