Redirect customer after stripe flow completion

main
MiguelMLorente 2025-11-24 22:15:07 +01:00
parent 7f2624b75a
commit f95cba9042
7 changed files with 72 additions and 19 deletions

View File

@ -1,6 +1,9 @@
import "./App.css"; import "./App.css";
import { Login } from "./pages/Login.tsx"; import { Login } from "./pages/Login.tsx";
import { ContentLayout, SpaceBetween } from "@cloudscape-design/components"; import {
ContentLayout,
SpaceBetween
} from "@cloudscape-design/components";
import icon from "./paella-icon.png"; import icon from "./paella-icon.png";
import { Outlet, Route, Routes } from "react-router"; import { Outlet, Route, Routes } from "react-router";
import { SignUp } from "./pages/SignUp.tsx"; import { SignUp } from "./pages/SignUp.tsx";
@ -10,9 +13,16 @@ import { useState } from "react";
import { PreLoginPageContext } from "./context/pre-login-page-context.ts"; import { PreLoginPageContext } from "./context/pre-login-page-context.ts";
import { LoggedInPageContext } from "./context/logged-in-page-context.ts"; import { LoggedInPageContext } from "./context/logged-in-page-context.ts";
import { NotFound } from "./pages/NotFound.tsx"; import { NotFound } from "./pages/NotFound.tsx";
import { BuyReturn } from "./pages/BuyReturn.tsx";
function App() { function App() {
const [userId, setUserId] = useState<number | null>(null); const locallyStoredUserId = window.localStorage.getItem("paysplit-user-id");
const locallyStoredUserIdAsNumber = locallyStoredUserId ? parseInt(locallyStoredUserId) : null
const [userId, setUserId] = useState<number | null>(locallyStoredUserIdAsNumber);
const storeUserId = (id: number) => {
window.localStorage.setItem("paysplit-user-id", id.toString());
setUserId(id);
}
return ( return (
<ContentLayout defaultPadding maxContentWidth={500}> <ContentLayout defaultPadding maxContentWidth={500}>
@ -21,7 +31,7 @@ function App() {
<Routes> <Routes>
<Route <Route
element={ element={
<PreLoginPageContext.Provider value={{ setUserId }}> <PreLoginPageContext.Provider value={{ setUserId: storeUserId }}>
<Outlet /> <Outlet />
</PreLoginPageContext.Provider> </PreLoginPageContext.Provider>
} }
@ -38,6 +48,7 @@ function App() {
> >
<Route path="/signup" element={<SignUp />} /> <Route path="/signup" element={<SignUp />} />
<Route path="/buy" element={<Buy />} /> <Route path="/buy" element={<Buy />} />
<Route path="/buy/return" element={<BuyReturn />} />
</Route> </Route>
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Routes> </Routes>

View File

@ -16,5 +16,12 @@ export const registerUser = (name: string, password: string) =>
password, password,
}) as Promise<AxiosResponse<AccessResponse, unknown, {}>>; }) as Promise<AxiosResponse<AccessResponse, unknown, {}>>;
export const startBuyFlow = (userId: string) => export const startBuyFlow = (userId: number, quantity: number) =>
axios.post("/buy/start", { userId }); axios.post("/buy/start", { userId, quantity }) as Promise<
AxiosResponse<{ url: string }, unknown, {}>
>;
export const completeBuyFlow = (userId: number) =>
axios.post("/buy/complete", { userId }) as Promise<
AxiosResponse<{ url: string }, unknown, {}>
>;

View File

@ -1,7 +1,7 @@
import { createContext, type Dispatch, type SetStateAction } from "react"; import { createContext } from "react";
interface PreLoginPageContextProps { interface PreLoginPageContextProps {
setUserId: Dispatch<SetStateAction<number | null>>; setUserId: (id: number) => void;
} }
export const PreLoginPageContext = export const PreLoginPageContext =

View File

@ -5,10 +5,18 @@ import {
Input, Input,
SpaceBetween, SpaceBetween,
} from "@cloudscape-design/components"; } from "@cloudscape-design/components";
import { useState } from "react"; import { useContext, useState } from "react";
import { startBuyFlow } from "../api/client";
import { LoggedInPageContext } from "../context/logged-in-page-context";
export const Buy = () => { export const Buy = () => {
const [quantity, setQuantity] = useState(0); const [quantity, setQuantity] = useState(0);
const { userId } = useContext(LoggedInPageContext)!;
const handleBuyFlow = () =>
startBuyFlow(userId, quantity).then(
(response) => (window.location.href = response.data.url),
);
return ( return (
<Container> <Container>
<SpaceBetween size="s"> <SpaceBetween size="s">
@ -18,7 +26,7 @@ export const Buy = () => {
value={quantity.toString()} value={quantity.toString()}
onChange={(e) => setQuantity(parseInt(e.detail.value))} onChange={(e) => setQuantity(parseInt(e.detail.value))}
/> />
<Button>Buy</Button> <Button onClick={handleBuyFlow}>Buy</Button>
</SpaceBetween> </SpaceBetween>
</Container> </Container>
); );

18
src/pages/BuyReturn.tsx Normal file
View File

@ -0,0 +1,18 @@
import { Header, SpaceBetween, Spinner } from "@cloudscape-design/components";
import { useContext } from "react";
import { completeBuyFlow } from "../api/client";
import { LoggedInPageContext } from "../context/logged-in-page-context";
export const BuyReturn = () => {
const { userId } = useContext(LoggedInPageContext)!;
completeBuyFlow(userId).then(
(response) => (window.location.href = response.data.url),
);
return (
<SpaceBetween size="m" direction="horizontal">
<Spinner size="big" />
<Header>We are processing your request</Header>
</SpaceBetween>
);
};

View File

@ -7,6 +7,7 @@ import {
} from "@cloudscape-design/components"; } from "@cloudscape-design/components";
import { useState } from "react"; import { useState } from "react";
import { FormattedDate } from "react-intl"; import { FormattedDate } from "react-intl";
import { Link } from "react-router";
export const SignUp = () => { export const SignUp = () => {
const [selectedDate, setSelectedDate] = useState<string | null>(null); const [selectedDate, setSelectedDate] = useState<string | null>(null);
@ -50,10 +51,13 @@ export const SignUp = () => {
{!availableTokens && ( {!availableTokens && (
<Container> <Container>
<p> <p>
You don't have any more tokens to sign up to any slot, please buy You don't have any tokens to sign up to a slot, please buy here.
here.
</p> </p>
<Button>Buy</Button> <Button>
<Link to="/buy" style={{ textDecoration: "none" }}>
Buy
</Link>
</Button>
</Container> </Container>
)} )}
</SpaceBetween> </SpaceBetween>

View File

@ -1,16 +1,21 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
const serverRoutes = ["/access", "/buy/start", "/buy/complete", "/buy/cancel"];
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
server: { server: {
proxy: { proxy: Object.fromEntries(
"/access": { serverRoutes.map((key) => [
target: "http://localhost:3000", key,
changeOrigin: true, {
secure: false, target: "http://localhost:3000",
}, changeOrigin: true,
}, secure: false,
},
]),
),
}, },
plugins: [react()], plugins: [react()],
}); });