mirror of
https://github.com/coaidev/coai.git
synced 2025-05-28 09:20:18 +09:00
feat: all in one payment method
This commit is contained in:
parent
e9a0ee1cd3
commit
b3dbef5b48
@ -26,6 +26,8 @@ import { Plus } from "lucide-react";
|
||||
import { subscriptionPrize } from "@/conf.ts";
|
||||
import { ToastAction } from "@/components/ui/toast.tsx";
|
||||
import { deeptrainEndpoint, useDeeptrain } from "@/utils/env.ts";
|
||||
import { AppDispatch } from "@/store";
|
||||
import { openDialog } from "@/store/quota.ts";
|
||||
|
||||
function countPrize(base: number, month: number): number {
|
||||
const prize = subscriptionPrize[base] * month;
|
||||
@ -57,6 +59,7 @@ type UpgradeProps = {
|
||||
async function callBuyAction(
|
||||
t: any,
|
||||
toast: any,
|
||||
dispatch: AppDispatch,
|
||||
month: number,
|
||||
level: number,
|
||||
): Promise<boolean> {
|
||||
@ -75,15 +78,22 @@ async function callBuyAction(
|
||||
action: (
|
||||
<ToastAction
|
||||
altText={t("buy.go")}
|
||||
onClick={() => (location.href = `${deeptrainEndpoint}/home/wallet`)}
|
||||
onClick={() =>
|
||||
useDeeptrain
|
||||
? (location.href = `${deeptrainEndpoint}/home/wallet`)
|
||||
: dispatch(openDialog())
|
||||
}
|
||||
>
|
||||
{t("buy.go")}
|
||||
</ToastAction>
|
||||
),
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.open(`${deeptrainEndpoint}/home/wallet`);
|
||||
}, 2000);
|
||||
|
||||
useDeeptrain
|
||||
? setTimeout(() => {
|
||||
window.open(`${deeptrainEndpoint}/home/wallet`);
|
||||
}, 2000)
|
||||
: dispatch(openDialog());
|
||||
}
|
||||
return res.status;
|
||||
}
|
||||
@ -179,7 +189,7 @@ export function Upgrade({ base, level }: UpgradeProps) {
|
||||
<Button
|
||||
className={`mb-1.5`}
|
||||
onClick={async () => {
|
||||
const res = await callBuyAction(t, toast, month, base);
|
||||
const res = await callBuyAction(t, toast, dispatch, month, base);
|
||||
if (res) {
|
||||
setOpen(false);
|
||||
await refreshSubscription(dispatch);
|
||||
|
@ -39,7 +39,7 @@ import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { useEffectAsync } from "@/utils/hook.ts";
|
||||
import { selectAuthenticated } from "@/store/auth.ts";
|
||||
import { ToastAction } from "@/components/ui/toast.tsx";
|
||||
import { deeptrainEndpoint, docsEndpoint } from "@/utils/env.ts";
|
||||
import { deeptrainEndpoint, docsEndpoint, useDeeptrain } from "@/utils/env.ts";
|
||||
|
||||
type AmountComponentProps = {
|
||||
amount: number;
|
||||
@ -238,10 +238,10 @@ function QuotaDialog() {
|
||||
} else {
|
||||
toast({
|
||||
title: t("buy.failed"),
|
||||
description: t("buy.failed-prompt", {
|
||||
description: `${t("buy.failed-prompt", {
|
||||
amount,
|
||||
}),
|
||||
action: (
|
||||
})}\n${res.error}`,
|
||||
action: useDeeptrain ? (
|
||||
<ToastAction
|
||||
altText={t("buy.go")}
|
||||
onClick={() =>
|
||||
@ -250,13 +250,14 @@ function QuotaDialog() {
|
||||
>
|
||||
{t("buy.go")}
|
||||
</ToastAction>
|
||||
),
|
||||
) : undefined,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.open(
|
||||
`${deeptrainEndpoint}/home/wallet`,
|
||||
);
|
||||
}, 2000);
|
||||
useDeeptrain &&
|
||||
setTimeout(() => {
|
||||
window.open(
|
||||
`${deeptrainEndpoint}/home/wallet`,
|
||||
);
|
||||
}, 2000);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -145,7 +145,7 @@
|
||||
"success": "购买成功",
|
||||
"success-prompt": "您已成功购买 {{amount}} 点数。",
|
||||
"failed": "购买失败",
|
||||
"failed-prompt": "购买点数失败。请确保您有足够的余额,您即将跳转到钱包支付余额。",
|
||||
"failed-prompt": "购买点数失败,请确保您有足够的余额。",
|
||||
"gpt4-tip": "提示:web 联网版功能可能会带来更多的输入点数消耗",
|
||||
"go": "前往"
|
||||
},
|
||||
@ -221,7 +221,7 @@
|
||||
"migrate-success": "变更成功",
|
||||
"migrate-success-prompt": "您已成功变更订阅计划。",
|
||||
"failed": "订阅失败",
|
||||
"failed-prompt": "订阅失败,请确保您有足够的余额,您即将跳转到钱包支付余额。",
|
||||
"failed-prompt": "订阅失败,请确保您有足够的余额。",
|
||||
"migrate-failed": "变更失败",
|
||||
"migrate-failed-prompt": "您的订阅变更失败。"
|
||||
},
|
||||
@ -402,12 +402,12 @@
|
||||
"retry-tip": "当渠道请求失败时,最多重试的次数",
|
||||
"model": "模型",
|
||||
"secret": "密钥",
|
||||
"secret-placeholder": "请输入密钥,格式:{{format}}\\n多个密钥时,一行一个,请求时随机选取负载",
|
||||
"secret-placeholder": "请输入密钥,格式:{{format}}\n多个密钥时,一行一个,请求时随机选取负载",
|
||||
"endpoint": "接入点",
|
||||
"endpoint-placeholder": "请输入接入点(即代理)",
|
||||
"mapper": "模型映射",
|
||||
"mapper-tip": "模型名转换,实现非对称的模型请求",
|
||||
"mapper-placeholder": "请输入模型映射,一行一个,格式: model>model\\n前者为请求的模型,后者为映射的模型(需要在模型中存在),中间用 > 分隔\\n格式前加!表示原模型不包含在此渠道的可用范围内,如: !gpt-4-slow>gpt-4,那么 gpt-4 将不会被涵盖在此渠道的可请求模型中",
|
||||
"mapper-placeholder": "请输入模型映射,一行一个,格式: model>model\n前者为请求的模型,后者为映射的模型(需要在模型中存在),中间用 > 分隔\n格式前加!表示原模型不包含在此渠道的可用范围内,如: !gpt-4-slow>gpt-4,那么 gpt-4 将不会被涵盖在此渠道的可请求模型中",
|
||||
"group": "用户分组",
|
||||
"group-tip": "用户分组,未包含的分组将不包含在此渠道的可用范围内 (分组为空时,所有用户都可以使用此渠道)",
|
||||
"state": "状态",
|
||||
@ -456,7 +456,7 @@
|
||||
"mailFrom": "发件人",
|
||||
"searchEndpoint": "搜索接入点",
|
||||
"searchQuery": "最大搜索结果数",
|
||||
"searchTip": "DuckDuckGo 搜索接入点,如不填写自动使用 WebPilot 和 New Bing 逆向进行搜索功能。\\nDuckDuckGo API 项目搭建:[duckduckgo-api](https://github.com/binjie09/duckduckgo-api)。"
|
||||
"searchTip": "DuckDuckGo 搜索接入点,如不填写自动使用 WebPilot 和 New Bing 逆向进行搜索功能。\nDuckDuckGo API 项目搭建:[duckduckgo-api](https://github.com/binjie09/duckduckgo-api)。"
|
||||
}
|
||||
},
|
||||
"mask": {
|
||||
|
@ -106,7 +106,7 @@
|
||||
"success": "Purchase successful",
|
||||
"success-prompt": "You have successfully purchased {{amount}} points.",
|
||||
"failed": "Purchase failed",
|
||||
"failed-prompt": "Failed to purchase points. Please make sure you have enough balance, you will soon jump to wallet to pay balance.",
|
||||
"failed-prompt": "Failed to purchase points, please make sure you have enough balance.",
|
||||
"gpt4-tip": "Tip: web searching feature may consume more input points",
|
||||
"go": "Go"
|
||||
},
|
||||
@ -182,7 +182,7 @@
|
||||
"migrate-success": "Migrate success",
|
||||
"migrate-success-prompt": "You have successfully migrated subscription.",
|
||||
"failed": "Subscribe failed",
|
||||
"failed-prompt": "Failed to subscribe, please make sure you have enough balance, you will soon jump to wallet to pay balance.",
|
||||
"failed-prompt": "Failed to subscribe, please make sure you have enough balance.",
|
||||
"migrate-failed": "Migrate failed",
|
||||
"migrate-failed-prompt": "Your subscription migration failed."
|
||||
},
|
||||
@ -464,4 +464,4 @@
|
||||
},
|
||||
"reset": "Reset",
|
||||
"request-error": "Request failed for {{reason}}"
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@
|
||||
"success": "購入が完了しました",
|
||||
"success-prompt": "{{amount}}クレジットを正常に購入しました。",
|
||||
"failed": "購入できませんでした",
|
||||
"failed-prompt": "クレジットの購入に失敗しました。十分な残高があることを確認し、残高を支払うためにウォレットにジャンプしようとしています。",
|
||||
"failed-prompt": "クレジットの購入に失敗しました。十分な残高があることを確認。",
|
||||
"gpt4-tip": "ヒント:ウェブに接続された機能により、より多くの入力ポイントが消費される可能性があります",
|
||||
"go": "行く"
|
||||
},
|
||||
@ -182,7 +182,7 @@
|
||||
"migrate-success": "変更に成功しました",
|
||||
"migrate-success-prompt": "サブスクリプションプランが正常に変更されました。",
|
||||
"failed": "サブスクリプションに失敗しました",
|
||||
"failed-prompt": "サブスクリプションに失敗しました。十分な残高があることを確認してください。残高を支払うためにウォレットにジャンプしようとしています。",
|
||||
"failed-prompt": "サブスクリプションに失敗しました。十分な残高があることを確認してください。",
|
||||
"migrate-failed": "変更できませんでした",
|
||||
"migrate-failed-prompt": "サブスクリプションの変更に失敗しました。"
|
||||
},
|
||||
@ -464,4 +464,4 @@
|
||||
},
|
||||
"reset": "リセット",
|
||||
"request-error": "{{reason}}のためにリクエストできませんでした"
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@
|
||||
"success": "Покупка прошла успешно",
|
||||
"success-prompt": "Вы успешно приобрели {{amount}} очков.",
|
||||
"failed": "Покупка не удалась",
|
||||
"failed-prompt": "Не удалось приобрести очки. Пожалуйста, убедитесь, что у вас достаточно баланса, вы скоро перейдете в кошелек для оплаты баланса.",
|
||||
"failed-prompt": "Не удалось приобрести очки. Пожалуйста, убедитесь, что у вас достаточно баланса.",
|
||||
"gpt4-tip": "Совет: функция веб-поиска может потреблять больше входных очков",
|
||||
"go": "Перейти к"
|
||||
},
|
||||
@ -182,7 +182,7 @@
|
||||
"migrate-success": "Перенос подписки успешен",
|
||||
"migrate-success-prompt": "Вы успешно перенесли подписку.",
|
||||
"failed": "Подписка не удалась",
|
||||
"failed-prompt": "Не удалось подписаться, пожалуйста, убедитесь, что у вас достаточно баланса, вы скоро перейдете в кошелек для оплаты баланса.",
|
||||
"failed-prompt": "Не удалось подписаться, пожалуйста, убедитесь, что у вас достаточно баланса.",
|
||||
"migrate-failed": "Перенос подписки не удался",
|
||||
"migrate-failed-prompt": "Ваша подписка не удалась."
|
||||
},
|
||||
@ -464,4 +464,4 @@
|
||||
},
|
||||
"reset": "сброс",
|
||||
"request-error": "Запрос не выполнен по {{reason}}"
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ export default defineConfig({
|
||||
},
|
||||
build: {
|
||||
manifest: true,
|
||||
chunkSizeWarningLimit: 2048,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: `assets/[name].[hash].js`,
|
||||
|
@ -386,7 +386,7 @@ func BuyAPI(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if BuyQuota(db, cache, user, form.Quota) {
|
||||
if err := BuyQuota(db, cache, user, form.Quota); err == nil {
|
||||
c.JSON(200, gin.H{
|
||||
"status": true,
|
||||
"error": "success",
|
||||
@ -394,7 +394,7 @@ func BuyAPI(c *gin.Context) {
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
"status": false,
|
||||
"error": "not enough money",
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"chat/admin"
|
||||
"chat/utils"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/spf13/viper"
|
||||
@ -74,19 +75,29 @@ func Pay(username string, amount float32) bool {
|
||||
return resp.Type
|
||||
}
|
||||
|
||||
func (u *User) Pay(cache *redis.Client, amount float32) bool {
|
||||
state := Pay(u.Username, amount)
|
||||
if state {
|
||||
admin.IncrBillingRequest(cache, int64(amount*100))
|
||||
func (u *User) Pay(db *sql.DB, cache *redis.Client, amount float32) bool {
|
||||
if useDeeptrain() {
|
||||
state := Pay(u.Username, amount)
|
||||
if state {
|
||||
admin.IncrBillingRequest(cache, int64(amount*100))
|
||||
}
|
||||
return state
|
||||
}
|
||||
return state
|
||||
|
||||
return u.PayedQuotaAsAmount(db, amount)
|
||||
}
|
||||
|
||||
func BuyQuota(db *sql.DB, cache *redis.Client, user *User, quota int) bool {
|
||||
func BuyQuota(db *sql.DB, cache *redis.Client, user *User, quota int) error {
|
||||
money := float32(quota) * 0.1
|
||||
if user.Pay(cache, money) {
|
||||
user.IncreaseQuota(db, float32(quota))
|
||||
return true
|
||||
|
||||
if !useDeeptrain() {
|
||||
return errors.New("cannot find payment provider")
|
||||
}
|
||||
return false
|
||||
|
||||
if user.Pay(db, cache, money) {
|
||||
user.IncreaseQuota(db, float32(quota))
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("do not have enough money")
|
||||
}
|
||||
|
@ -62,3 +62,23 @@ func (u *User) UseQuota(db *sql.DB, quota float32) bool {
|
||||
}
|
||||
return u.IncreaseUsedQuota(db, quota)
|
||||
}
|
||||
|
||||
func (u *User) PayedQuota(db *sql.DB, quota float32) bool {
|
||||
if quota == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
current := u.GetQuota(db)
|
||||
if quota > current {
|
||||
return false
|
||||
}
|
||||
|
||||
if !u.DecreaseQuota(db, quota) {
|
||||
return false
|
||||
}
|
||||
return u.IncreaseUsedQuota(db, quota)
|
||||
}
|
||||
|
||||
func (u *User) PayedQuotaAsAmount(db *sql.DB, amount float32) bool {
|
||||
return u.PayedQuota(db, amount*10)
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func BuySubscription(db *sql.DB, cache *redis.Client, user *User, level int, mon
|
||||
if before == 0 || before == level {
|
||||
// buy new subscription or renew subscription
|
||||
money := CountSubscriptionPrize(level, month)
|
||||
if user.Pay(cache, money) {
|
||||
if user.Pay(db, cache, money) {
|
||||
// migrate subscription
|
||||
user.AddSubscription(db, month, level)
|
||||
|
||||
@ -165,7 +165,7 @@ func BuySubscription(db *sql.DB, cache *redis.Client, user *User, level int, mon
|
||||
} else {
|
||||
// upgrade subscription
|
||||
money := user.CountUpgradePrice(db, level)
|
||||
if user.Pay(cache, money) {
|
||||
if user.Pay(db, cache, money) {
|
||||
user.SetSubscriptionLevel(db, level)
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user