mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat: Stripe connect using OAuth
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { useSetStripeAccountCallback } from '@/hooks/query/stripe-integration';
|
||||
|
||||
function useQuery() {
|
||||
const { search } = useLocation();
|
||||
|
||||
return React.useMemo(() => new URLSearchParams(search), [search]);
|
||||
}
|
||||
|
||||
export default function PreferencesStripeCallback() {
|
||||
const query = useQuery();
|
||||
const code = query.get('code') as string;
|
||||
const { mutateAsync: stripeAccountCallback } = useSetStripeAccountCallback();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
stripeAccountCallback({ code }).then(() => {
|
||||
history.push('/preferences/payment-methods')
|
||||
});
|
||||
}, [history, stripeAccountCallback, code]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -10,8 +10,9 @@ import {
|
||||
Popover,
|
||||
Tag,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from '@blueprintjs/core';
|
||||
import { AppToaster, Box, Card, Group, Stack } from '@/components';
|
||||
import { Box, Card, Group, Stack } from '@/components';
|
||||
import { StripeLogo } from '@/icons/StripeLogo';
|
||||
import { usePaymentMethodsBoot } from './PreferencesPaymentMethodsBoot';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
useDialogActions,
|
||||
useDrawerActions,
|
||||
} from '@/hooks/state';
|
||||
import { useCreateStripeAccountLink } from '@/hooks/query/stripe-integration';
|
||||
import { DRAWERS } from '@/constants/drawers';
|
||||
import { MoreIcon } from '@/icons/More';
|
||||
import { STRIPE_PRICING_LINK } from './constants';
|
||||
@@ -34,39 +34,17 @@ export function StripePaymentMethod() {
|
||||
const stripeState = paymentMethodsState?.stripe;
|
||||
|
||||
const isAccountCreated = stripeState?.isStripeAccountCreated;
|
||||
const isAccountActive = stripeState?.isStripePaymentActive;
|
||||
const stripeAccountId = stripeState?.stripeAccountId;
|
||||
const isPaymentEnabled = stripeState?.isStripePaymentEnabled;
|
||||
const isPayoutEnabled = stripeState?.isStripePayoutEnabled;
|
||||
const isStripeEnabled = stripeState?.isStripeEnabled;
|
||||
const stripePaymentMethodId = stripeState?.stripePaymentMethodId;
|
||||
const isStripeServerConfigured = stripeState?.isStripeServerConfigured;
|
||||
|
||||
const {
|
||||
mutateAsync: createStripeAccountLink,
|
||||
isLoading: isCreateStripeLinkLoading,
|
||||
} = useCreateStripeAccountLink();
|
||||
|
||||
// Handle Stripe setup button click.
|
||||
const handleSetUpBtnClick = () => {
|
||||
openDialog(DialogsName.StripeSetup);
|
||||
};
|
||||
|
||||
// Handle complete Stripe setup button click.
|
||||
const handleCompleteSetUpBtnClick = () => {
|
||||
createStripeAccountLink({ stripeAccountId })
|
||||
.then((res) => {
|
||||
const { clientSecret } = res;
|
||||
|
||||
if (clientSecret.url) {
|
||||
window.open(clientSecret.url, '_blank');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
AppToaster.show({
|
||||
message: 'Something went wrong.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Handle edit button click.
|
||||
const handleEditBtnClick = () => {
|
||||
openDrawer(DRAWERS.STRIPE_PAYMENT_INTEGRATION_EDIT, {
|
||||
@@ -87,14 +65,30 @@ export function StripePaymentMethod() {
|
||||
<Group>
|
||||
<StripeLogo />
|
||||
|
||||
{isAccountActive && (
|
||||
<Tag minimal intent={Intent.SUCCESS}>
|
||||
Active
|
||||
</Tag>
|
||||
)}
|
||||
<Group spacing={10}>
|
||||
{isStripeEnabled && (
|
||||
<Tag minimal intent={Intent.SUCCESS}>
|
||||
Active
|
||||
</Tag>
|
||||
)}
|
||||
{!isPaymentEnabled && isAccountCreated && (
|
||||
<Tooltip content="The account cannot accept payments because verification may be incomplete, there may be legal or compliance issues, or required documents haven't been submitted or verified.">
|
||||
<Tag minimal intent={Intent.DANGER}>
|
||||
Payment Not Enabled
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isPayoutEnabled && isAccountCreated && (
|
||||
<Tooltip content="The account cannot receive payouts due to incomplete or invalid bank details, pending identity verification, or compliance restrictions.">
|
||||
<Tag minimal intent={Intent.DANGER}>
|
||||
Payout Not Enabled
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
<Group spacing={10}>
|
||||
{isAccountActive && (
|
||||
{isAccountCreated && (
|
||||
<Button small onClick={handleEditBtnClick}>
|
||||
Edit
|
||||
</Button>
|
||||
@@ -104,16 +98,6 @@ export function StripePaymentMethod() {
|
||||
Set it Up
|
||||
</Button>
|
||||
)}
|
||||
{isAccountCreated && !isAccountActive && (
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
small
|
||||
onClick={handleCompleteSetUpBtnClick}
|
||||
loading={isCreateStripeLinkLoading}
|
||||
>
|
||||
Complete Stripe Set Up
|
||||
</Button>
|
||||
)}
|
||||
{isAccountCreated && (
|
||||
<Popover
|
||||
content={
|
||||
@@ -153,7 +137,7 @@ export function StripePaymentMethod() {
|
||||
{!isStripeServerConfigured && (
|
||||
<Text style={{ color: '#CD4246' }}>
|
||||
Stripe payment is not configured from the server.{' '}
|
||||
</Text>
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</PaymentFooter>
|
||||
|
||||
@@ -1,53 +1,32 @@
|
||||
import { useState } from 'react';
|
||||
import { Button, DialogBody, DialogFooter, Intent } from '@blueprintjs/core';
|
||||
import styled from 'styled-components';
|
||||
import { Stack } from '@/components';
|
||||
import { useDialogContext } from '@/components/Dialog/DialogProvider';
|
||||
import {
|
||||
useCreateStripeAccount,
|
||||
useCreateStripeAccountLink,
|
||||
} from '@/hooks/query/stripe-integration';
|
||||
import { useDialogActions } from '@/hooks/state';
|
||||
import { CreditCard2Icon } from '@/icons/CreditCard2';
|
||||
import { DollarIcon } from '@/icons/Dollar';
|
||||
import { LayoutAutoIcon } from '@/icons/LayoutAuto';
|
||||
import { SwitchIcon } from '@/icons/SwitchIcon';
|
||||
import { usePaymentMethodsBoot } from '../../PreferencesPaymentMethodsBoot';
|
||||
|
||||
export function StripePreSetupDialogContent() {
|
||||
const { name } = useDialogContext();
|
||||
const { closeDialog } = useDialogActions();
|
||||
|
||||
const {
|
||||
mutateAsync: createStripeAccount,
|
||||
isLoading: isCreateStripeAccountLoading,
|
||||
} = useCreateStripeAccount();
|
||||
|
||||
const {
|
||||
mutateAsync: createStripeAccountLink,
|
||||
isLoading: isCreateStripeLinkLoading,
|
||||
} = useCreateStripeAccountLink();
|
||||
const { paymentMethodsState } = usePaymentMethodsBoot();
|
||||
const [isRedirecting, setIsRedirecting] = useState<boolean>(false);
|
||||
|
||||
const handleSetUpBtnClick = () => {
|
||||
createStripeAccount({})
|
||||
.then((response) => {
|
||||
const { account_id: accountId } = response;
|
||||
|
||||
return createStripeAccountLink({ stripeAccountId: accountId });
|
||||
})
|
||||
.then((res) => {
|
||||
const { clientSecret } = res;
|
||||
|
||||
if (clientSecret.url) {
|
||||
window.location.href = clientSecret.url;
|
||||
}
|
||||
});
|
||||
if (paymentMethodsState?.stripe.stripeAuthLink) {
|
||||
setIsRedirecting(true);
|
||||
window.location.href = paymentMethodsState?.stripe.stripeAuthLink;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = () => {
|
||||
closeDialog(name);
|
||||
};
|
||||
|
||||
const isLoading = isCreateStripeAccountLoading || isCreateStripeLinkLoading;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogBody>
|
||||
@@ -92,7 +71,7 @@ export function StripePreSetupDialogContent() {
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSetUpBtnClick}
|
||||
loading={isLoading}
|
||||
loading={isRedirecting}
|
||||
>
|
||||
Set Up Stripe
|
||||
</Button>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Button, Intent } from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||
import { useDrawerActions } from '@/hooks/state';
|
||||
import { ACCOUNT_TYPE } from '@/constants';
|
||||
|
||||
export function StripeIntegrationEditFormContent() {
|
||||
const { accounts } = useStripeIntegrationEditBoot();
|
||||
@@ -19,6 +20,7 @@ export function StripeIntegrationEditFormContent() {
|
||||
<AccountsSelect
|
||||
name={'bankAccountId'}
|
||||
items={accounts}
|
||||
filterByTypes={[ACCOUNT_TYPE.CASH, ACCOUNT_TYPE.BANK]}
|
||||
fastField
|
||||
fill
|
||||
allowCreate
|
||||
@@ -35,6 +37,7 @@ export function StripeIntegrationEditFormContent() {
|
||||
<AccountsSelect
|
||||
name={'clearingAccountId'}
|
||||
items={accounts}
|
||||
filterByTypes={[ACCOUNT_TYPE.OTHER_CURRENT_LIABILITY]}
|
||||
fastField
|
||||
fill
|
||||
allowCreate
|
||||
|
||||
@@ -108,6 +108,7 @@ export function transformToEditForm(invoice) {
|
||||
: TaxType.Exclusive,
|
||||
entries,
|
||||
attachments: transformAttachmentsToForm(invoice),
|
||||
payment_methods: transformPaymentMethodsToForm(invoice?.payment_methods),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -228,6 +229,11 @@ export function transformValueToRequest(values) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the form payment methods to request.
|
||||
* @param {Record<string, { enable: boolean }>} paymentMethods
|
||||
* @returns {Array<{ payment_integration_id: string; enable: boolean }>}
|
||||
*/
|
||||
const transformPaymentMethodsToRequest = (
|
||||
paymentMethods: Record<string, { enable: boolean }>,
|
||||
): Array<{ payment_integration_id: string; enable: boolean }> => {
|
||||
@@ -237,6 +243,20 @@ const transformPaymentMethodsToRequest = (
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes payment methods from request to form.
|
||||
* @param {Array<{ payment_integration_id: number; enable: boolean }>} paymentMethods
|
||||
* @returns {Record<string, { enable: boolean }>}
|
||||
*/
|
||||
const transformPaymentMethodsToForm = (
|
||||
paymentMethods: Array<{ payment_integration_id: number; enable: boolean }>,
|
||||
): Record<string, { enable: boolean }> => {
|
||||
return paymentMethods?.reduce((acc, method) => {
|
||||
acc[method.payment_integration_id] = { enable: method.enable };
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
export const useSetPrimaryWarehouseToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { warehouses, isWarehousesSuccess } = useInvoiceFormContext();
|
||||
|
||||
Reference in New Issue
Block a user