feat: add stripe topup page
This commit is contained in:
@@ -28,7 +28,6 @@ var stripeAdaptor = &StripeAdaptor{}
|
|||||||
type StripePayRequest struct {
|
type StripePayRequest struct {
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
PaymentMethod string `json:"payment_method"`
|
PaymentMethod string `json:"payment_method"`
|
||||||
TopUpCode string `json:"top_up_code"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StripeAdaptor struct {
|
type StripeAdaptor struct {
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ const TopUp = () => {
|
|||||||
statusState?.status?.enable_online_topup || false,
|
statusState?.status?.enable_online_topup || false,
|
||||||
);
|
);
|
||||||
const [priceRatio, setPriceRatio] = useState(statusState?.status?.price || 1);
|
const [priceRatio, setPriceRatio] = useState(statusState?.status?.price || 1);
|
||||||
|
|
||||||
|
const [stripeAmount, setStripeAmount] = useState(0.0);
|
||||||
|
const [stripeMinTopUp, setStripeMinTopUp] = useState(statusState?.status?.stripe_min_topup || 1);
|
||||||
|
const [stripeTopUpCount, setStripeTopUpCount] = useState(statusState?.status?.stripe_min_topup || 1);
|
||||||
|
const [enableStripeTopUp, setEnableStripeTopUp] = useState(statusState?.status?.enable_stripe_topup || false);
|
||||||
|
const [stripeOpen, setStripeOpen] = useState(false);
|
||||||
|
|
||||||
const [userQuota, setUserQuota] = useState(0);
|
const [userQuota, setUserQuota] = useState(0);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -231,6 +238,65 @@ const TopUp = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stripePreTopUp = async () => {
|
||||||
|
if (!enableStripeTopUp) {
|
||||||
|
showError(t('管理员未开启在线充值!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPayWay('stripe')
|
||||||
|
setPaymentLoading(true);
|
||||||
|
try {
|
||||||
|
await getStripeAmount();
|
||||||
|
if (stripeTopUpCount < stripeMinTopUp) {
|
||||||
|
showError(t('充值数量不能小于') + stripeMinTopUp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStripeOpen(true);
|
||||||
|
} catch (error) {
|
||||||
|
showError(t('获取金额失败'));
|
||||||
|
} finally {
|
||||||
|
setPayWay('')
|
||||||
|
setPaymentLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onlineStripeTopUp = async () => {
|
||||||
|
if (stripeAmount === 0) {
|
||||||
|
await getStripeAmount();
|
||||||
|
}
|
||||||
|
if (stripeTopUpCount < stripeMinTopUp) {
|
||||||
|
showError(t('充值数量不能小于') + stripeMinTopUp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setConfirmLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await API.post('/api/user/stripe/pay', {
|
||||||
|
amount: parseInt(stripeTopUpCount),
|
||||||
|
payment_method: 'stripe',
|
||||||
|
});
|
||||||
|
if (res !== undefined) {
|
||||||
|
const { message, data } = res.data;
|
||||||
|
if (message === 'success') {
|
||||||
|
processStripeCallback(data)
|
||||||
|
} else {
|
||||||
|
showError(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showError(res);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
showError(t('支付请求失败'));
|
||||||
|
} finally {
|
||||||
|
setStripeOpen(false);
|
||||||
|
setConfirmLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processStripeCallback = (data) => {
|
||||||
|
location.href = data.pay_link;
|
||||||
|
};
|
||||||
|
|
||||||
const getUserQuota = async () => {
|
const getUserQuota = async () => {
|
||||||
setUserDataLoading(true);
|
setUserDataLoading(true);
|
||||||
let res = await API.get(`/api/user/self`);
|
let res = await API.get(`/api/user/self`);
|
||||||
@@ -327,6 +393,10 @@ const TopUp = () => {
|
|||||||
setTopUpLink(statusState.status.top_up_link || '');
|
setTopUpLink(statusState.status.top_up_link || '');
|
||||||
setEnableOnlineTopUp(statusState.status.enable_online_topup || false);
|
setEnableOnlineTopUp(statusState.status.enable_online_topup || false);
|
||||||
setPriceRatio(statusState.status.price || 1);
|
setPriceRatio(statusState.status.price || 1);
|
||||||
|
|
||||||
|
setStripeMinTopUp(statusState.status.stripe_min_topup || 1);
|
||||||
|
setStripeTopUpCount(statusState.status.stripe_min_topup || 1);
|
||||||
|
setEnableStripeTopUp(statusState.status.enable_stripe_topup || false);
|
||||||
}
|
}
|
||||||
}, [statusState?.status]);
|
}, [statusState?.status]);
|
||||||
|
|
||||||
@@ -334,6 +404,10 @@ const TopUp = () => {
|
|||||||
return amount + ' ' + t('元');
|
return amount + ' ' + t('元');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderStripeAmount = () => {
|
||||||
|
return stripeAmount + ' ' + t('元');
|
||||||
|
};
|
||||||
|
|
||||||
const getAmount = async (value) => {
|
const getAmount = async (value) => {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
value = topUpCount;
|
value = topUpCount;
|
||||||
@@ -361,10 +435,40 @@ const TopUp = () => {
|
|||||||
setAmountLoading(false);
|
setAmountLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getStripeAmount = async (value) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
value = stripeTopUpCount
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await API.post('/api/user/stripe/amount', {
|
||||||
|
amount: parseFloat(value),
|
||||||
|
});
|
||||||
|
if (res !== undefined) {
|
||||||
|
const { message, data } = res.data;
|
||||||
|
// showInfo(message);
|
||||||
|
if (message === 'success') {
|
||||||
|
setStripeAmount(parseFloat(data))
|
||||||
|
} else {
|
||||||
|
setStripeAmount( 0)
|
||||||
|
Toast.error({ content: '错误:' + data, id: 'getAmount' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showError(res);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleStripeCancel = () => {
|
||||||
|
setStripeOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
const handleTransferCancel = () => {
|
const handleTransferCancel = () => {
|
||||||
setOpenTransfer(false);
|
setOpenTransfer(false);
|
||||||
};
|
};
|
||||||
@@ -374,6 +478,9 @@ const TopUp = () => {
|
|||||||
setTopUpCount(preset.value);
|
setTopUpCount(preset.value);
|
||||||
setSelectedPreset(preset.value);
|
setSelectedPreset(preset.value);
|
||||||
setAmount(preset.value * priceRatio);
|
setAmount(preset.value * priceRatio);
|
||||||
|
|
||||||
|
setStripeTopUpCount(preset.value);
|
||||||
|
setStripeAmount(preset.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化大数字显示
|
// 格式化大数字显示
|
||||||
@@ -496,6 +603,25 @@ const TopUp = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title={t('确定要充值吗')}
|
||||||
|
visible={stripeOpen}
|
||||||
|
onOk={onlineStripeTopUp}
|
||||||
|
onCancel={handleStripeCancel}
|
||||||
|
maskClosable={false}
|
||||||
|
size='small'
|
||||||
|
centered
|
||||||
|
confirmLoading={confirmLoading}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{t('充值数量')}:{stripeTopUpCount}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('实付金额')}:{renderStripeAmount()}
|
||||||
|
</p>
|
||||||
|
<p>{t('是否确认充值?')}</p>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<div className='grid grid-cols-1 lg:grid-cols-12 gap-6'>
|
<div className='grid grid-cols-1 lg:grid-cols-12 gap-6'>
|
||||||
{/* 左侧充值区域 */}
|
{/* 左侧充值区域 */}
|
||||||
<div className='lg:col-span-7 space-y-6 w-full'>
|
<div className='lg:col-span-7 space-y-6 w-full'>
|
||||||
@@ -798,7 +924,7 @@ const TopUp = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!enableOnlineTopUp && (
|
{!enableOnlineTopUp && !enableStripeTopUp && (
|
||||||
<Banner
|
<Banner
|
||||||
type='warning'
|
type='warning'
|
||||||
description={t(
|
description={t(
|
||||||
@@ -809,6 +935,99 @@ const TopUp = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{enableStripeTopUp && (
|
||||||
|
<>
|
||||||
|
{/* 桌面端显示的自定义金额和支付按钮 */}
|
||||||
|
<div className='hidden md:block space-y-4'>
|
||||||
|
{!enableOnlineTopUp?(
|
||||||
|
<Divider style={{ margin: '24px 0' }}>
|
||||||
|
<Text className='text-sm font-medium'>
|
||||||
|
{t('或输入自定义金额')}
|
||||||
|
</Text>
|
||||||
|
</Divider>
|
||||||
|
) : (
|
||||||
|
<Divider style={{ margin: '24px 0' }}>
|
||||||
|
<Text className='text-sm font-medium'>
|
||||||
|
{t('Stripe')}
|
||||||
|
</Text>
|
||||||
|
</Divider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className='flex justify-between mb-2'>
|
||||||
|
<Text strong>{t('充值数量')}</Text>
|
||||||
|
{amountLoading ? (
|
||||||
|
<Skeleton.Title
|
||||||
|
style={{ width: '80px', height: '16px' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text type='tertiary'>
|
||||||
|
{t('实付金额:') + renderStripeAmount()}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<InputNumber
|
||||||
|
disabled={!enableOnlineTopUp}
|
||||||
|
placeholder={
|
||||||
|
t('充值数量,最低 ') + renderQuotaWithAmount(stripeMinTopUp)
|
||||||
|
}
|
||||||
|
value={stripeTopUpCount}
|
||||||
|
min={stripeMinTopUp}
|
||||||
|
max={999999999}
|
||||||
|
step={1}
|
||||||
|
precision={0}
|
||||||
|
onChange={async (value) => {
|
||||||
|
if (value && value >= 1) {
|
||||||
|
setStripeTopUpCount(value);
|
||||||
|
setSelectedPreset(null);
|
||||||
|
await getStripeAmount(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
const value = parseInt(e.target.value);
|
||||||
|
if (!value || value < 1) {
|
||||||
|
setStripeTopUpCount(1);
|
||||||
|
getStripeAmount(1);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size='large'
|
||||||
|
className='w-full'
|
||||||
|
formatter={(value) => (value ? `${value}` : '')}
|
||||||
|
parser={(value) =>
|
||||||
|
value ? parseInt(value.replace(/[^\d]/g, '')) : 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Text strong className='block mb-3'>
|
||||||
|
{t('选择支付方式')}
|
||||||
|
</Text>
|
||||||
|
<div className='grid grid-cols-1 gap-3'>
|
||||||
|
<Button
|
||||||
|
key='stripe'
|
||||||
|
type='primary'
|
||||||
|
onClick={() => stripePreTopUp()}
|
||||||
|
size='large'
|
||||||
|
disabled={!enableStripeTopUp}
|
||||||
|
loading={paymentLoading && payWay === 'stripe'}
|
||||||
|
icon={<CreditCard size={16} />}
|
||||||
|
style={{
|
||||||
|
height: '40px',
|
||||||
|
color: '#b161fe',
|
||||||
|
}}
|
||||||
|
className='transition-all hover:shadow-md w-full'
|
||||||
|
>
|
||||||
|
<span className='ml-1'>Stripe</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Divider style={{ margin: '24px 0' }}>
|
<Divider style={{ margin: '24px 0' }}>
|
||||||
<Text className='text-sm font-medium'>{t('兑换码充值')}</Text>
|
<Text className='text-sm font-medium'>{t('兑换码充值')}</Text>
|
||||||
</Divider>
|
</Divider>
|
||||||
|
|||||||
Reference in New Issue
Block a user