diff --git a/src/app/config.ts b/src/app/config.ts index e8b8353f..42a6373c 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -3,6 +3,30 @@ const BASE_URL = process.env.REACT_APP_RELEASE ? `${location.origin}/api` : `https://dev.voce.chat/api`; +export const LicensePriceList = [ + { + title: "VoceChat Pro", + limit: 100, + pid: "price_1LkICIGGoUDRyc3jqrdPEnkY", + desc: "User Limit: 100" + }, + { + title: "VoceChat Supreme", + limit: 99999, + pid: "price_1LkKqdGGoUDRyc3jnpZvXhzx", + desc: "User Limit: No Limit" + } + // { + // title: "VoceChat Enterprise", + // limit: 500, + // // pid: "price_1LkJNsGGoUDRyc3jkVNf4VPE", + // pid: "price_1LkQGpGGoUDRyc3jGTh3GYHw", + // desc: "User Limit: 500" + // } +]; +export const PAYMENT_URL_PREFIX = process.env.REACT_APP_RELEASE + ? `https://vera.nicegoodthings.com` + : `http://localhost:4000`; export const CACHE_VERSION = `0.3.1`; export const GuestRoutes = ["/", "/chat", "/chat/channel/:channel_id"]; export const ContentTypes = { diff --git a/src/app/services/server.ts b/src/app/services/server.ts index 0c750b50..ab34be1d 100644 --- a/src/app/services/server.ts +++ b/src/app/services/server.ts @@ -1,5 +1,5 @@ import { createApi } from "@reduxjs/toolkit/query/react"; -import BASE_URL from "../config"; +import BASE_URL, { PAYMENT_URL_PREFIX } from "../config"; import { updateInfo } from "../slices/server"; import baseQuery from "./base.query"; import { RootState } from "../store"; @@ -14,7 +14,9 @@ import { SMTPConfig, AgoraConfig, GithubAuthConfig, - LicenseResponse + LicenseResponse, + RenewLicense, + RenewLicenseResponse } from "../../types/server"; const defaultExpireDuration = 7 * 24 * 60 * 60; @@ -195,6 +197,18 @@ export const serverApi = createApi({ }) }), + getLicensePaymentUrl: builder.mutation({ + query: (data) => ({ + url: `${PAYMENT_URL_PREFIX}/vocechat/payment/create`, + method: "POST", + body: data + }) + }), + getGeneratedLicense: builder.query<{ license: string }, string>({ + query: (session_id) => ({ + url: `${PAYMENT_URL_PREFIX}/vocechat/licenses/${session_id}` + }) + }), checkLicense: builder.mutation({ query: (license) => ({ url: "/license/check", @@ -241,5 +255,7 @@ export const { useCreateAdminMutation, useUpsertLicenseMutation, useCheckLicenseMutation, - useGetLicenseQuery + useGetLicenseQuery, + useGetLicensePaymentUrlMutation, + useLazyGetGeneratedLicenseQuery } = serverApi; diff --git a/src/assets/icons/check.png b/src/assets/icons/check.png new file mode 100644 index 00000000..21c30465 Binary files /dev/null and b/src/assets/icons/check.png differ diff --git a/src/common/hook/useLicense.ts b/src/common/hook/useLicense.ts index 07a9c096..8c090b96 100644 --- a/src/common/hook/useLicense.ts +++ b/src/common/hook/useLicense.ts @@ -4,8 +4,10 @@ import { useGetLicenseQuery, useUpsertLicenseMutation } from "../../app/services/server"; +import { useAppSelector } from "../../app/store"; const useLicense = () => { + const userCount = useAppSelector((store) => store.users.ids.length); const { data: license } = useGetLicenseQuery(); const [check, { isLoading: isChecking, isSuccess: checked }] = useCheckLicenseMutation(); const [upsert, { isSuccess: upserted, isLoading: upserting }] = useUpsertLicenseMutation(); @@ -21,8 +23,10 @@ const useLicense = () => { return false; } }; - + console.log("uuu", userCount, license); + const lUserLimit = license?.user_limit ?? 0; return { + reachLimit: userCount >= lUserLimit, license, checked, checking: isChecking, diff --git a/src/routes/callback/PaymentSuccess.tsx b/src/routes/callback/PaymentSuccess.tsx new file mode 100644 index 00000000..e1f56c97 --- /dev/null +++ b/src/routes/callback/PaymentSuccess.tsx @@ -0,0 +1,71 @@ +import { useEffect } from "react"; +import styled from "styled-components"; +import { useLazyGetGeneratedLicenseQuery } from "../../app/services/server"; +import useLicense from "../../common/hook/useLicense"; +import Button from "../../common/component/styled/Button"; +import checkIcon from "../../assets/icons/check.png"; +import { useNavigate } from "react-router-dom"; +const Styled = styled.section` + display: flex; + flex-direction: column; + align-items: center; + padding: 24px; + width: 512px; + background: #f3f4f6; + border-radius: 20px; + .check { + width: 120px; + height: 120px; + } + .head { + font-weight: bold; + font-size: 32px; + padding-top: 20px; + } + .desc { + font-size: 18px; + color: #999; + padding: 0 0 30px 0; + } +`; + +type Props = { + sid: string; +}; + +const PaymentSuccess = ({ sid }: Props) => { + const navigateTo = useNavigate(); + const { upsertLicense, upserting, upserted } = useLicense(); + const [getGeneratedLicense, { data, isError, isLoading, isSuccess }] = + useLazyGetGeneratedLicenseQuery(); + useEffect(() => { + if (sid) { + getGeneratedLicense(sid); + } + }, [sid]); + useEffect(() => { + if (isSuccess && data) { + const l = data.license; + upsertLicense(l); + } + }, [data, isSuccess]); + const handleBack = () => { + navigateTo("/"); + }; + return ( + + check icon +

Payment Success!

+

+ {upserting ? "Renewing the License, do not close the window!" : ""} + {upserted ? "Renew the License Successfully!" : ""} + {isError ? "Invalided Stripe Session ID" : ""} +

+ +
+ ); +}; + +export default PaymentSuccess; diff --git a/src/routes/callback/index.tsx b/src/routes/callback/index.tsx new file mode 100644 index 00000000..8c08380e --- /dev/null +++ b/src/routes/callback/index.tsx @@ -0,0 +1,18 @@ +import { useParams } from "react-router-dom"; +import PaymentSuccess from "./PaymentSuccess"; +import StyledWrapper from "./styled"; +// type Props = { +// type: "payment_success"; +// }; +// 该页面服务于一些第三方服务的回调,比如stripe付款成功的回调 +export default function CallbackPage() { + const { type = "", payload = "" } = useParams(); + if (type == "payment_success") { + return ( + + + + ); + } + return callback page; +} diff --git a/src/routes/callback/styled.tsx b/src/routes/callback/styled.tsx new file mode 100644 index 00000000..b3eb1582 --- /dev/null +++ b/src/routes/callback/styled.tsx @@ -0,0 +1,13 @@ +import styled from "styled-components"; + +const StyledWrapper = styled.div` + display: flex; + width: 100vw; + height: 100vh; + justify-content: center; + align-items: center; + word-break: break-word; + line-height: 1.5; +`; + +export default StyledWrapper; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 55112189..587b36e1 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -14,6 +14,7 @@ const RegPage = lazy(() => import("./reg/Register")); const LoginPage = lazy(() => import("./login")); const OAuthPage = lazy(() => import("./oauth")); const UsersPage = lazy(() => import("./users")); +const CallbackPage = lazy(() => import("./callback")); const FavoritesPage = lazy(() => import("./favs")); const OnboardingPage = lazy(() => import("./onboarding")); const InvitePage = lazy(() => import("./invite")); @@ -51,6 +52,7 @@ const PageRoutes = () => { }> } /> + } /> } /> .input { + > .license { width: 100%; display: flex; - flex-direction: column; + /* flex-direction: column; */ align-items: flex-start; - gap: 8px; + gap: 15px; } `; export default function License() { - const { license: licenseInfo, checking, upserting, upsertLicense } = useLicense(); - const [license, setLicense] = useState(""); - const handleUpsert = async () => { - if (!license) { - toast.error("License Empty"); - hideAll(); - return; - } - const success = await upsertLicense(license); - if (!success) { - toast.error("Invalid License"); - hideAll(); - } else { - toast.success("License Updated!"); - } + const { license: licenseInfo } = useLicense(); + const [modalVisible, setModalVisible] = useState(false); + const handleRenewLicense = () => { + toggleModalVisible(); }; - const handleLicenseInput = (evt: ChangeEvent) => { - setLicense(evt.target.value); + const toggleModalVisible = () => { + setModalVisible((prev) => !prev); }; - useEffect(() => { - if (licenseInfo?.base58) { - setLicense(licenseInfo.base58); - } - }, [licenseInfo]); - const disableBtn = checking || upserting || !license || license === licenseInfo?.base58; + // const disableBtn = !reachLimit; return ( - -
- -
- Signed - {licenseInfo?.sign ? "Yes" : "Not Yet"} -
-
- Domains -
    - {" "} - {licenseInfo?.domains.map((d) => { - return
  • {d}
  • ; - })} -
-
-
- User Limit - {licenseInfo?.user_limit} -
-
- Expired At - - {dayjs(licenseInfo?.expired_at).format("YYYY-MM-DD h:mm:ss A")} - -
-
- Created At - - {dayjs(licenseInfo?.created_at).format("YYYY-MM-DD h:mm:ss A")} - -
- - } - > -