mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
feat: Onboard accounts to Stripe Connect
This commit is contained in:
@@ -54,6 +54,11 @@ export default [
|
||||
disabled: false,
|
||||
href: '/preferences/items',
|
||||
},
|
||||
{
|
||||
text: 'Integrations',
|
||||
disabled: false,
|
||||
href: '/preferences/integrations'
|
||||
},
|
||||
// {
|
||||
// text: <T id={'sms_integration.label'} />,
|
||||
// disabled: false,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { StripeIntegration } from '@/containers/StripePayment/StripeIntegration';
|
||||
|
||||
export default function IntegrationsPage() {
|
||||
return <StripeIntegration />
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
ConnectAccountOnboarding,
|
||||
ConnectComponentsProvider,
|
||||
} from '@stripe/react-connect-js';
|
||||
import { useStripeConnect } from './use-stripe-connect';
|
||||
import { useCreateStripeAccount } from '@/hooks/query/stripe-integration';
|
||||
|
||||
export function StripeIntegration() {
|
||||
const [accountCreatePending, setAccountCreatePending] =
|
||||
useState<boolean>(false);
|
||||
const [onboardingExited, setOnboardingExited] = useState<boolean>(false);
|
||||
const [error, setError] = useState<boolean>(false);
|
||||
const [connectedAccountId, setConnectedAccountId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const stripeConnectInstance = useStripeConnect(connectedAccountId || '');
|
||||
|
||||
const { mutateAsync: createAccount } = useCreateStripeAccount();
|
||||
|
||||
const handleSignupBtnClick = () => {
|
||||
setAccountCreatePending(true);
|
||||
setError(false);
|
||||
|
||||
createAccount({})
|
||||
.then((account) => {
|
||||
setConnectedAccountId(account.account_id);
|
||||
})
|
||||
.catch(() => {
|
||||
setError(true);
|
||||
})
|
||||
.finally(() => {
|
||||
setAccountCreatePending(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="banner">
|
||||
<h2>Bigcapital Technology, Inc.</h2>
|
||||
</div>
|
||||
<div className="content">
|
||||
{!connectedAccountId && <h2>Get ready for take off</h2>}
|
||||
{connectedAccountId && !stripeConnectInstance && (
|
||||
<h2>Add information to start accepting money</h2>
|
||||
)}
|
||||
{!connectedAccountId && (
|
||||
<p>
|
||||
Bigcapital Technology, Inc. is the world's leading air travel
|
||||
platform: join our team of pilots to help people travel faster.
|
||||
</p>
|
||||
)}
|
||||
{!accountCreatePending && !connectedAccountId && (
|
||||
<div>
|
||||
<button onClick={handleSignupBtnClick}>Sign up</button>
|
||||
</div>
|
||||
)}
|
||||
{stripeConnectInstance && (
|
||||
<ConnectComponentsProvider connectInstance={stripeConnectInstance}>
|
||||
<ConnectAccountOnboarding
|
||||
onExit={() => setOnboardingExited(true)}
|
||||
/>
|
||||
</ConnectComponentsProvider>
|
||||
)}
|
||||
{error && <p className="error">Something went wrong!</p>}
|
||||
{(connectedAccountId || accountCreatePending || onboardingExited) && (
|
||||
<div className="dev-callout">
|
||||
{connectedAccountId && (
|
||||
<p>
|
||||
Your connected account ID is:{' '}
|
||||
<code className="bold">{connectedAccountId}</code>
|
||||
</p>
|
||||
)}
|
||||
{accountCreatePending && <p>Creating a connected account...</p>}
|
||||
{onboardingExited && (
|
||||
<p>The Account Onboarding component has exited</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="info-callout">
|
||||
<p>
|
||||
This is a sample app for Connect onboarding using the Account
|
||||
Onboarding embedded component.{' '}
|
||||
<a
|
||||
href="https://docs.stripe.com/connect/onboarding/quickstart?connect-onboarding-surface=embedded"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
View docs
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
loadConnectAndInitialize,
|
||||
StripeConnectInstance,
|
||||
} from '@stripe/connect-js';
|
||||
import { useCreateStripeAccountSession } from '@/hooks/query/stripe-integration';
|
||||
|
||||
export const useStripeConnect = (connectedAccountId?: string) => {
|
||||
const [stripeConnectInstance, setStripeConnectInstance] =
|
||||
useState<StripeConnectInstance | null>();
|
||||
const { mutateAsync: createAccountSession } = useCreateStripeAccountSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (connectedAccountId) {
|
||||
const fetchClientSecret = async (): Promise<string> => {
|
||||
try {
|
||||
const clientSecret = await createAccountSession({
|
||||
connectedAccountId,
|
||||
});
|
||||
return clientSecret?.client_secret as string;
|
||||
} catch (error) {
|
||||
// Handle errors on the client side here
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`An error occurred: ${error.message}`);
|
||||
} else {
|
||||
throw new Error('An unknown error occurred');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setStripeConnectInstance(
|
||||
loadConnectAndInitialize({
|
||||
publishableKey: 'pk_test_51PRck9BW396nDn7gxEw1uvkoGwl5BXDWnrhntQIWReiDnH2Zdm7uL0RSvzKN6SR6ELHDK99dF9UbVEumgTu8k0oN00pP0J91Lx',
|
||||
fetchClientSecret,
|
||||
appearance: {
|
||||
overlays: 'dialog',
|
||||
variables: {
|
||||
colorPrimary: '#ffffff',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [connectedAccountId, createAccountSession]);
|
||||
|
||||
return stripeConnectInstance;
|
||||
};
|
||||
59
packages/webapp/src/hooks/query/stripe-integration.ts
Normal file
59
packages/webapp/src/hooks/query/stripe-integration.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
// @ts-nocheck
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions,
|
||||
UseMutationResult,
|
||||
} from 'react-query';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
interface AccountSessionValues {
|
||||
connectedAccountId?: string;
|
||||
}
|
||||
interface AccountSessionResponse {
|
||||
client_secret: string;
|
||||
}
|
||||
|
||||
export const useCreateStripeAccountSession = (
|
||||
options?: UseMutationOptions<
|
||||
AccountSessionResponse,
|
||||
Error,
|
||||
AccountSessionValues
|
||||
>,
|
||||
): UseMutationResult<AccountSessionResponse, Error, AccountSessionValues> => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values: AccountSessionValues) => {
|
||||
return apiRequest
|
||||
.post('/stripe_integration/account_session', {
|
||||
account: values?.connectedAccountId,
|
||||
})
|
||||
.then((res) => res.data);
|
||||
},
|
||||
{ ...options },
|
||||
);
|
||||
};
|
||||
|
||||
interface CreateStripeAccountValues {}
|
||||
interface CreateStripeAccountResponse {
|
||||
account_id: string;
|
||||
}
|
||||
|
||||
export const useCreateStripeAccount = (
|
||||
options?: UseMutationOptions<
|
||||
CreateStripeAccountResponse,
|
||||
Error,
|
||||
CreateStripeAccountValues
|
||||
>,
|
||||
) => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values: CreateStripeAccountValues) => {
|
||||
return apiRequest
|
||||
.post('/stripe_integration/account')
|
||||
.then((res) => res.data);
|
||||
},
|
||||
{ ...options },
|
||||
);
|
||||
};
|
||||
@@ -103,6 +103,13 @@ export const getPreferenceRoutes = () => [
|
||||
component: lazy(() => import('@/containers/Subscriptions/BillingPage')),
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${BASE_URL}/integrations`,
|
||||
component: lazy(
|
||||
() => import('@/containers/Preferences/Integrations/IntegrationsPage'),
|
||||
),
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${BASE_URL}/`,
|
||||
component: lazy(() => import('../containers/Preferences/DefaultRoute')),
|
||||
|
||||
Reference in New Issue
Block a user