Merge pull request #1006 from automatisch/aut-73
feat: add checkout process
This commit is contained in:
@@ -39,7 +39,7 @@ type AppConfig = {
|
||||
smtpPassword: string;
|
||||
fromEmail: string;
|
||||
isCloud: boolean;
|
||||
paddleVendorId: string;
|
||||
paddleVendorId: number;
|
||||
paddleVendorAuthCode: string;
|
||||
stripeSecretKey: string;
|
||||
stripeSigningSecret: string;
|
||||
@@ -113,7 +113,7 @@ const appConfig: AppConfig = {
|
||||
smtpPassword: process.env.SMTP_PASSWORD,
|
||||
fromEmail: process.env.FROM_EMAIL,
|
||||
isCloud: process.env.AUTOMATISCH_CLOUD === 'true',
|
||||
paddleVendorId: process.env.PADDLE_VENDOR_ID,
|
||||
paddleVendorId: Number(process.env.PADDLE_VENDOR_ID),
|
||||
paddleVendorAuthCode: process.env.PADDLE_VENDOR_AUTH_CODE,
|
||||
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
|
||||
stripeSigningSecret: process.env.STRIPE_SIGNING_SECRET,
|
||||
|
@@ -485,7 +485,7 @@ type GetPaymentPortalUrl {
|
||||
|
||||
type GetPaddleInfo {
|
||||
sandbox: Boolean
|
||||
vendorId: String
|
||||
vendorId: Int
|
||||
}
|
||||
|
||||
type PaymentPlan {
|
||||
|
@@ -16,14 +16,26 @@ import Paper from '@mui/material/Paper';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
|
||||
import usePaymentPlans from 'hooks/usePaymentPlans.ee';
|
||||
import useCurrentUser from 'hooks/useCurrentUser';
|
||||
import usePaddle from 'hooks/usePaddle.ee';
|
||||
|
||||
export default function UpgradeFreeTrial() {
|
||||
const { plans, loading } = usePaymentPlans();
|
||||
const currentUser = useCurrentUser();
|
||||
const { loaded: paddleLoaded } = usePaddle();
|
||||
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
||||
const selectedPlan = plans?.[selectedIndex];
|
||||
|
||||
const updateSelection = (index: number) => setSelectedIndex(index);
|
||||
|
||||
const handleCheckout = React.useCallback(() => {
|
||||
window.Paddle.Checkout?.open({
|
||||
product: selectedPlan.productId,
|
||||
email: currentUser.email,
|
||||
passthrough: JSON.stringify({ id: currentUser.id, email: currentUser.email })
|
||||
})
|
||||
}, [selectedPlan, currentUser]);
|
||||
|
||||
if (loading || !plans.length) return null;
|
||||
|
||||
return (
|
||||
@@ -140,7 +152,13 @@ export default function UpgradeFreeTrial() {
|
||||
<Typography variant="subtitle2" sx={{ fontSize: '12px', mt: 0 }}>
|
||||
+ VAT if applicable
|
||||
</Typography>
|
||||
<Button size="small" variant="contained" sx={{ mt: 2 }}>
|
||||
<Button
|
||||
size="small"
|
||||
variant="contained"
|
||||
sx={{ mt: 2 }}
|
||||
onClick={handleCheckout}
|
||||
disabled={!paddleLoaded}
|
||||
>
|
||||
<LockIcon fontSize="small" sx={{ mr: 1 }} />
|
||||
Pay securely via Paddle
|
||||
</Button>
|
||||
|
72
packages/web/src/contexts/Paddle.ee.tsx
Normal file
72
packages/web/src/contexts/Paddle.ee.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import useCloud from 'hooks/useCloud';
|
||||
import usePaddleInfo from 'hooks/usePaddleInfo.ee';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
Paddle: any;
|
||||
}
|
||||
}
|
||||
|
||||
export type PaddleContextParams = {
|
||||
loaded: boolean;
|
||||
};
|
||||
|
||||
export const PaddleContext =
|
||||
React.createContext<PaddleContextParams>({
|
||||
loaded: false,
|
||||
});
|
||||
|
||||
type PaddleProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PaddleProvider = (
|
||||
props: PaddleProviderProps
|
||||
): React.ReactElement => {
|
||||
const { children } = props;
|
||||
const isCloud = useCloud();
|
||||
const { sandbox, vendorId } = usePaddleInfo();
|
||||
const [loaded, setLoaded] = React.useState(false);
|
||||
|
||||
const value = React.useMemo(() => {
|
||||
return {
|
||||
loaded,
|
||||
};
|
||||
}, [loaded]);
|
||||
|
||||
React.useEffect(function loadPaddleScript() {
|
||||
if (!isCloud) return;
|
||||
|
||||
const g = document.createElement('script')
|
||||
const s = document.getElementsByTagName('script')[0];
|
||||
g.src = 'https://cdn.paddle.com/paddle/paddle.js';
|
||||
g.defer = true;
|
||||
g.async = true;
|
||||
|
||||
if (s.parentNode) {
|
||||
s.parentNode.insertBefore(g, s);
|
||||
}
|
||||
|
||||
g.onload = function () {
|
||||
setLoaded(true);
|
||||
}
|
||||
}, [isCloud]);
|
||||
|
||||
React.useEffect(function initPaddleScript() {
|
||||
if (!loaded || !vendorId) return;
|
||||
|
||||
if (sandbox) {
|
||||
window.Paddle.Environment.set('sandbox');
|
||||
}
|
||||
|
||||
window.Paddle.Setup({ vendor: vendorId });
|
||||
}, [loaded, sandbox, vendorId])
|
||||
|
||||
return (
|
||||
<PaddleContext.Provider value={value}>
|
||||
{children}
|
||||
</PaddleContext.Provider>
|
||||
);
|
||||
};
|
10
packages/web/src/graphql/queries/get-paddle-info.ee.ts
Normal file
10
packages/web/src/graphql/queries/get-paddle-info.ee.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_PADDLE_INFO = gql`
|
||||
query GetPaddleInfo {
|
||||
getPaddleInfo {
|
||||
sandbox
|
||||
vendorId
|
||||
}
|
||||
}
|
||||
`;
|
14
packages/web/src/hooks/usePaddle.ee.ts
Normal file
14
packages/web/src/hooks/usePaddle.ee.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { PaddleContext } from 'contexts/Paddle.ee';
|
||||
|
||||
type UsePaddleReturn = {
|
||||
loaded: boolean;
|
||||
};
|
||||
|
||||
export default function usePaddle(): UsePaddleReturn {
|
||||
const paddleContext = React.useContext(PaddleContext);
|
||||
|
||||
return {
|
||||
loaded: paddleContext.loaded,
|
||||
};
|
||||
}
|
19
packages/web/src/hooks/usePaddleInfo.ee.ts
Normal file
19
packages/web/src/hooks/usePaddleInfo.ee.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { GET_PADDLE_INFO } from 'graphql/queries/get-paddle-info.ee';
|
||||
|
||||
type UsePaddleInfoReturn = {
|
||||
sandbox: boolean;
|
||||
vendorId: string;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
export default function usePaddleInfo(): UsePaddleInfoReturn {
|
||||
const { data, loading } = useQuery(GET_PADDLE_INFO);
|
||||
|
||||
return {
|
||||
sandbox: data?.getPaddleInfo?.sandbox,
|
||||
vendorId: data?.getPaddleInfo?.vendorId,
|
||||
loading
|
||||
};
|
||||
}
|
@@ -6,6 +6,7 @@ import ApolloProvider from 'components/ApolloProvider';
|
||||
import SnackbarProvider from 'components/SnackbarProvider';
|
||||
import { AuthenticationProvider } from 'contexts/Authentication';
|
||||
import { AutomatischInfoProvider } from 'contexts/AutomatischInfo';
|
||||
import { PaddleProvider } from 'contexts/Paddle.ee';
|
||||
import Router from 'components/Router';
|
||||
import LiveChat from 'components/LiveChat/index.ee';
|
||||
import routes from 'routes';
|
||||
@@ -18,11 +19,13 @@ ReactDOM.render(
|
||||
<ApolloProvider>
|
||||
<AutomatischInfoProvider>
|
||||
<IntlProvider>
|
||||
<ThemeProvider>
|
||||
{routes}
|
||||
<PaddleProvider>
|
||||
<ThemeProvider>
|
||||
{routes}
|
||||
|
||||
<LiveChat />
|
||||
</ThemeProvider>
|
||||
<LiveChat />
|
||||
</ThemeProvider>
|
||||
</PaddleProvider>
|
||||
</IntlProvider>
|
||||
</AutomatischInfoProvider>
|
||||
</ApolloProvider>
|
||||
|
Reference in New Issue
Block a user