replace epay with stripe

Signed-off-by: wozulong <>
This commit is contained in:
wozulong
2024-03-22 18:00:20 +08:00
parent 0907fa6994
commit 247ae0988f
15 changed files with 533 additions and 329 deletions

View File

@@ -21,13 +21,13 @@ const SystemSetting = () => {
SMTPFrom: '',
SMTPToken: '',
ServerAddress: '',
EpayId: '',
EpayKey: '',
Price: 7.3,
MinTopUp: 1,
StripeApiSecret: '',
StripeWebhookSecret: '',
StripePriceId: '',
PaymentEnabled: false,
StripeUnitPrice: 8.0,
MinTopUp: 5,
TopupGroupRatio: '',
PayAddress: '',
CustomCallbackAddress: '',
Footer: '',
WeChatAuthEnabled: '',
WeChatServerAddress: '',
@@ -92,6 +92,7 @@ const SystemSetting = () => {
case 'TurnstileCheckEnabled':
case 'EmailDomainRestrictionEnabled':
case 'RegisterEnabled':
case 'PaymentEnabled':
value = inputs[key] === 'true' ? 'false' : 'true';
break;
default:
@@ -106,9 +107,6 @@ const SystemSetting = () => {
if (key === 'EmailDomainWhitelist') {
value = value.split(',');
}
if (key === 'Price') {
value = parseFloat(value);
}
setInputs((inputs) => ({
...inputs, [key]: value
}));
@@ -128,10 +126,11 @@ const SystemSetting = () => {
name === 'Notice' ||
name.startsWith('SMTP') ||
name === 'ServerAddress' ||
name === 'EpayId' ||
name === 'EpayKey' ||
name === 'Price' ||
name === 'PayAddress' ||
name === 'StripeApiSecret' ||
name === 'StripeWebhookSecret' ||
name === 'StripePriceId' ||
name === 'StripeUnitPrice' ||
name === 'MinTopUp' ||
name === 'GitHubClientId' ||
name === 'GitHubClientSecret' ||
name === 'LinuxDoClientId' ||
@@ -158,7 +157,7 @@ const SystemSetting = () => {
await updateOption('ServerAddress', ServerAddress);
};
const submitPayAddress = async () => {
const submitPaymentConfig = async () => {
if (inputs.ServerAddress === '') {
showError('请先填写服务器地址');
return;
@@ -170,15 +169,30 @@ const SystemSetting = () => {
}
await updateOption('TopupGroupRatio', inputs.TopupGroupRatio);
}
let PayAddress = removeTrailingSlash(inputs.PayAddress);
await updateOption('PayAddress', PayAddress);
if (inputs.EpayId !== '') {
await updateOption('EpayId', inputs.EpayId);
let stripeApiSecret = removeTrailingSlash(inputs.StripeApiSecret);
if (stripeApiSecret && !stripeApiSecret.startsWith("sk_")) {
showError('输入了无效的Stripe API密钥');
return;
}
if (inputs.EpayKey !== '') {
await updateOption('EpayKey', inputs.EpayKey);
stripeApiSecret && await updateOption('StripeApiSecret', stripeApiSecret);
let stripeWebhookSecret = removeTrailingSlash(inputs.StripeWebhookSecret);
if (stripeWebhookSecret && !stripeWebhookSecret.startsWith("whsec_")) {
showError('输入了无效的Stripe Webhook签名密钥');
return;
}
await updateOption('Price', '' + inputs.Price);
stripeWebhookSecret && await updateOption('StripeWebhookSecret', stripeWebhookSecret);
let stripePriceId = removeTrailingSlash(inputs.StripePriceId);
if (stripePriceId && !stripePriceId.startsWith("price_")) {
showError('输入了无效的Stripe 物品价格ID');
return;
}
await updateOption('StripePriceId', stripePriceId);
await updateOption('PaymentEnable', inputs.PaymentEnabled);
await updateOption('StripeUnitPrice', inputs.StripeUnitPrice);
await updateOption('MinTopUp', inputs.MinTopUp);
};
const submitSMTP = async () => {
@@ -318,54 +332,66 @@ const SystemSetting = () => {
更新服务器地址
</Form.Button>
<Divider />
<Header as="h3">支付设置当前仅支持易支付接口默认使用上方服务器地址作为回调地址</Header>
<Header as="h3">
支付设置当前仅支持Stripe Checkout
<Header.Subheader>
密钥Webhook 等设置请
<a href="https://dashboard.stripe.com/developers" target="_blank" rel="noreferrer">
点击此处
</a>
进行设置最好先在
<a href="https://dashboard.stripe.com/test/developers" target="_blank" rel="noreferrer">
测试环境
</a>
进行测试
</Header.Subheader>
</Header>
<Message>
Webhook
<code>{`${inputs.ServerAddress}/api/stripe/webhook`}</code>
需要包含事件<code>checkout.session.completed</code> <code>checkout.session.expired</code>
</Message>
<Form.Group widths="equal">
<Form.Input
label="支付地址,不填写则不启用在线支付"
placeholder="例如https://yourdomain.com"
value={inputs.PayAddress}
name="PayAddress"
label="API密钥"
placeholder="sk_xxx的Stripe密钥敏感信息不显示"
value={inputs.StripeApiSecret}
name="StripeApiSecret"
onChange={handleInputChange}
/>
<Form.Input
label="易支付商户ID"
placeholder="例如0001"
value={inputs.EpayId}
name="EpayId"
label="Webhook签名密钥"
placeholder="whsec_xxx的Webhook签名密钥敏感信息不显示"
value={inputs.StripeWebhookSecret}
name="StripeWebhookSecret"
onChange={handleInputChange}
/>
<Form.Input
label="易支付商户密钥"
placeholder="例如dejhfueqhujasjmndbjkqaw"
value={inputs.EpayKey}
name="EpayKey"
label="商品价格ID"
placeholder="price_xxx的商品价格ID新建产品后可获得"
value={inputs.StripePriceId}
name="StripePriceId"
onChange={handleInputChange}
/>
</Form.Group>
<Form.Group widths="equal">
<Form.Input
label="回调地址,不填写则使用上方服务器地址作为回调地址"
placeholder="例如https://yourdomain.com"
value={inputs.CustomCallbackAddress}
name="CustomCallbackAddress"
onChange={handleInputChange}
label="商品单价(元)"
placeholder="商品的人民币价格"
value={inputs.StripeUnitPrice}
name="StripeUnitPrice"
type={"number"}
min={0}
onChange={handleInputChange}
/>
<Form.Input
label="充值价格x元/美金)"
placeholder="例如:7,就是7元/美金"
value={inputs.Price}
name="Price"
min={0}
onChange={handleInputChange}
/>
<Form.Input
label="最低充值数量"
placeholder="例如2就是最低充值2$"
value={inputs.MinTopUp}
name="MinTopUp"
min={1}
onChange={handleInputChange}
label="最低充值数量"
placeholder="例如:2,就是最低充值2件商品"
value={inputs.MinTopUp}
name="MinTopUp"
type={"number"}
min={1}
onChange={handleInputChange}
/>
</Form.Group>
<Form.Group widths="equal">
@@ -379,9 +405,17 @@ const SystemSetting = () => {
placeholder="为一个 JSON 文本,键为组名称,值为倍率"
/>
</Form.Group>
<Form.Button onClick={submitPayAddress}>
更新支付设置
</Form.Button>
<Form.Group inline>
<Form.Button onClick={submitPaymentConfig}>
更新支付设置
</Form.Button>
<Form.Checkbox
checked={inputs.PaymentEnabled === 'true'}
label="开启在线支付"
name="PaymentEnabled"
onChange={handleInputChange}
/>
</Form.Group>
<Divider />
<Header as="h3">配置登录注册</Header>
<Form.Group inline>

View File

@@ -126,6 +126,10 @@ export function openPage(url) {
}
export function removeTrailingSlash(url) {
if (!url) {
return "";
}
if (url.endsWith('/')) {
return url.slice(0, -1);
} else {

View File

@@ -11,18 +11,20 @@ const TopUp = () => {
const [topUpCode, setTopUpCode] = useState('');
const [topUpCount, setTopUpCount] = useState(10);
const [minTopupCount, setMinTopUpCount] = useState(1);
const [amount, setAmount] = useState(0.0);
const [payAmount, setPayAmount] = useState(0.0);
const [chargedAmount, setChargedAmount] = useState(0.0);
const [minTopUp, setMinTopUp] = useState(1);
const [topUpLink, setTopUpLink] = useState('');
const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(false);
const [paymentEnabled, setPaymentEnabled] = useState(false);
const [userQuota, setUserQuota] = useState(0);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isPaying, setIsPaying] = useState(false);
const [open, setOpen] = useState(false);
const [payWay, setPayWay] = useState('');
const topUp = async () => {
if (redemptionCode === '') {
showInfo('请输入兑换码!')
showError('请输入兑换码!')
return;
}
setIsSubmitting(true);
@@ -57,15 +59,19 @@ const TopUp = () => {
};
const preTopUp = async (payment) => {
if (!enableOnlineTopUp) {
if (!paymentEnabled) {
showError('管理员未开启在线充值!');
return;
}
if (amount === 0) {
if (!Number.isInteger(Number(topUpCount))) {
showError('充值数量必须是整数!');
return;
}
if (payAmount === 0) {
await getAmount();
}
if (topUpCount < minTopUp) {
showInfo('充值数量不能小于' + minTopUp);
showError('充值数量不能小于' + minTopUp);
return;
}
setPayWay(payment)
@@ -73,15 +79,16 @@ const TopUp = () => {
}
const onlineTopUp = async () => {
if (amount === 0) {
if (payAmount === 0) {
await getAmount();
}
if (topUpCount < minTopUp) {
showInfo('充值数量不能小于' + minTopUp);
showError('充值数量不能小于' + minTopUp);
return;
}
setOpen(false);
try {
setIsPaying(true)
const res = await API.post('/api/user/pay', {
amount: parseInt(topUpCount),
top_up_code: topUpCode,
@@ -91,33 +98,13 @@ const TopUp = () => {
const {message, data} = res.data;
// showInfo(message);
if (message === 'success') {
let params = data
let url = res.data.url
let form = document.createElement('form')
form.action = url
form.method = 'POST'
// 判断是否为safari浏览器
let isSafari = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") < 1;
if (!isSafari) {
form.target = '_blank'
}
for (let key in params) {
let input = document.createElement('input')
input.type = 'hidden'
input.name = key
input.value = params[key]
form.appendChild(input)
}
document.body.appendChild(form)
form.submit()
document.body.removeChild(form)
location.href = data.payLink
} else {
setIsPaying(false)
showError(data);
// setTopUpCount(parseInt(res.data.count));
// setAmount(parseInt(data));
}
} else {
setIsPaying(false)
showError(res);
}
} catch (err) {
@@ -146,8 +133,8 @@ const TopUp = () => {
if (status.min_topup) {
setMinTopUp(status.min_topup);
}
if (status.enable_online_topup) {
setEnableOnlineTopUp(status.enable_online_topup);
if (status.payment_enabled) {
setPaymentEnabled(status.payment_enabled);
}
}
getUserQuota().then();
@@ -155,7 +142,7 @@ const TopUp = () => {
const renderAmount = () => {
// console.log(amount);
return amount + '元';
return payAmount + '元';
}
const getAmount = async (value) => {
@@ -171,7 +158,8 @@ const TopUp = () => {
const {message, data} = res.data;
// showInfo(message);
if (message === 'success') {
setAmount(parseFloat(data));
setPayAmount(parseFloat(data.payAmount));
setChargedAmount(parseFloat(data.chargedAmount));
} else {
showError(data);
// setTopUpCount(parseInt(res.data.count));
@@ -206,7 +194,7 @@ const TopUp = () => {
size={'small'}
centered={true}
>
<p>充值数量{topUpCount}$</p>
<p>充值数量{topUpCount}$实到{chargedAmount}$</p>
<p>实付金额{renderAmount()}</p>
<p>是否确认充值</p>
</Modal>
@@ -244,54 +232,50 @@ const TopUp = () => {
</Space>
</Form>
</div>
<div style={{marginTop: 20}}>
<Divider>
在线充值
</Divider>
<Form>
<Form.Input
disabled={!enableOnlineTopUp}
field={'redemptionCount'}
label={'实付金额:' + renderAmount()}
placeholder={'充值数量,最低' + minTopUp + '$'}
name='redemptionCount'
type={'number'}
value={topUpCount}
suffix={'$'}
min={minTopUp}
defaultValue={minTopUp}
max={100000}
onChange={async (value) => {
if (value < 1) {
value = 1;
}
if (value > 100000) {
value = 100000;
}
setTopUpCount(value);
await getAmount(value);
}}
/>
<Space>
<Button type={'primary'} theme={'solid'} onClick={
async () => {
preTopUp('zfb')
}
}>
支付宝
</Button>
<Button style={{backgroundColor: 'rgba(var(--semi-green-5), 1)'}}
type={'primary'}
theme={'solid'} onClick={
async () => {
preTopUp('wx')
}
}>
微信
</Button>
</Space>
</Form>
</div>
{paymentEnabled ?
<div style={{marginTop: 20}}>
<Divider>
在线充值
</Divider>
<Form>
<Form.Input
disabled={!paymentEnabled}
field={'redemptionCount'}
label={'实付金额:' + renderAmount()}
placeholder={'充值数量,必须整数,最低' + minTopUp + '$'}
name='redemptionCount'
type={'number'}
value={topUpCount}
suffix={'$'}
min={minTopUp}
defaultValue={minTopUp}
max={100000}
onChange={async (value) => {
if (value < 1) {
value = 1;
}
if (value > 100000) {
value = 100000;
}
setTopUpCount(value);
await getAmount(value);
}}
/>
<Space>
<Button style={{backgroundColor: '#2e75cd'}}
type={'primary'}
disabled={isPaying}
theme={'solid'} onClick={
async () => {
preTopUp('stripe')
}
}>
{isPaying ? '支付中...' : '去支付'}
</Button>
</Space>
</Form>
</div> : <></>
}
{/*<div style={{ display: 'flex', justifyContent: 'right' }}>*/}
{/* <Text>*/}
{/* <Link onClick={*/}