Merge pull request #1006 from automatisch/aut-73

feat: add checkout process
This commit is contained in:
Ömer Faruk Aydın
2023-03-21 11:34:16 +03:00
committed by GitHub
8 changed files with 144 additions and 8 deletions

View File

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

View File

@@ -485,7 +485,7 @@ type GetPaymentPortalUrl {
type GetPaddleInfo {
sandbox: Boolean
vendorId: String
vendorId: Int
}
type PaymentPlan {

View File

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

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

View File

@@ -0,0 +1,10 @@
import { gql } from '@apollo/client';
export const GET_PADDLE_INFO = gql`
query GetPaddleInfo {
getPaddleInfo {
sandbox
vendorId
}
}
`;

View 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,
};
}

View 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
};
}

View File

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