feat: all in one payment method

This commit is contained in:
Zhang Minghan 2023-12-26 00:18:18 +08:00
parent e9a0ee1cd3
commit b3dbef5b48
11 changed files with 86 additions and 43 deletions

View File

@ -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);

View File

@ -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);
}
}}
>

View File

@ -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": {

View File

@ -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}}"
}
}

View File

@ -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}}のためにリクエストできませんでした"
}
}

View File

@ -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}}"
}
}

View File

@ -27,6 +27,7 @@ export default defineConfig({
},
build: {
manifest: true,
chunkSizeWarningLimit: 2048,
rollupOptions: {
output: {
entryFileNames: `assets/[name].[hash].js`,

View File

@ -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(),
})
}
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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
}