feat: Stripe payment integration

This commit is contained in:
Ahmed Bouhuolia
2024-09-21 16:50:22 +02:00
parent 8de8695b25
commit 7756b5b304
24 changed files with 691 additions and 102 deletions

View File

@@ -78,7 +78,7 @@ export function PaymentPortal() {
<Group position={'apart'} className={styles.totalItem}>
<Text>Total</Text>
<Text style={{ fontWeight: 600 }}>
<Text style={{ fontWeight: 500 }}>
{sharableLinkMeta?.totalFormatted}
</Text>
</Group>
@@ -96,7 +96,7 @@ export function PaymentPortal() {
className={clsx(styles.totalItem, styles.borderBottomDark)}
>
<Text>Due Amount</Text>
<Text style={{ fontWeight: 600 }}>
<Text style={{ fontWeight: 500 }}>
{sharableLinkMeta?.dueAmountFormatted}
</Text>
</Group>

View File

@@ -1,5 +1,5 @@
import { StripeIntegration } from '@/containers/StripePayment/StripeIntegration';
import { StripeIntegration2 } from '@/containers/StripePayment/StripeIntegration';
export default function IntegrationsPage() {
return <StripeIntegration />
return <StripeIntegration2 />
}

View File

@@ -0,0 +1,40 @@
import React, { createContext, ReactNode, useContext } from 'react';
import { useGetPaymentServicesState } from '@/hooks/query/payment-services';
type PaymentMethodsContextType = {
isPaymentMethodsStateLoading: boolean;
paymentMethodsState: any;
};
const PaymentMethodsContext = createContext<PaymentMethodsContextType>(
{} as PaymentMethodsContextType,
);
type PaymentMethodsProviderProps = {
children: ReactNode;
};
const PaymentMethodsBoot = ({ children }: PaymentMethodsProviderProps) => {
const { data: paymentMethodsState, isLoading: isPaymentMethodsStateLoading } =
useGetPaymentServicesState();
const value = { isPaymentMethodsStateLoading, paymentMethodsState };
return (
<PaymentMethodsContext.Provider value={value}>
{children}
</PaymentMethodsContext.Provider>
);
};
const usePaymentMethodsBoot = () => {
const context = useContext<PaymentMethodsContextType>(PaymentMethodsContext);
if (context === undefined) {
throw new Error(
'usePaymentMethods must be used within a PaymentMethodsProvider',
);
}
return context;
};
export { PaymentMethodsBoot, usePaymentMethodsBoot };

View File

@@ -1,20 +1,23 @@
// @ts-nocheck
import styled from 'styled-components';
import { Button, Classes, Intent, Text } from '@blueprintjs/core';
import { Box, Card, Group, Stack } from '@/components';
import { StripeLogo } from '@/icons/StripeLogo';
import { Button, Classes, Intent, Text } from '@blueprintjs/core';
import { PaymentMethodsBoot } from './PreferencesPaymentMethodsBoot';
export default function PreferencesPaymentMethodsPage() {
return (
<PaymentMethodsRoot>
<Text className={Classes.TEXT_MUTED} style={{ marginBottom: 20 }}>
Accept payments from all the major debit and credit card networks
through the supported payment gateways.
</Text>
<PaymentMethodsBoot>
<Text className={Classes.TEXT_MUTED} style={{ marginBottom: 20 }}>
Accept payments from all the major debit and credit card networks
through the supported payment gateways.
</Text>
<Stack>
<StripePaymentMethod />
</Stack>
<Stack>
<StripePaymentMethod />
</Stack>
</PaymentMethodsBoot>
</PaymentMethodsRoot>
);
}

View File

@@ -1,37 +1,17 @@
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';
useCreateStripeAccount,
useCreateStripeAccountLink,
} 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);
});
};
export const StripeIntegration2 = () => {
const [accountCreatePending, setAccountCreatePending] = useState(false);
const [accountLinkCreatePending, setAccountLinkCreatePending] =
useState(false);
const [error, setError] = useState(false);
const [connectedAccountId, setConnectedAccountId] = useState<string>();
const { mutateAsync: createStripeAccount } = useCreateStripeAccount();
const { mutateAsync: createStripeAccountLink } = useCreateStripeAccountLink();
return (
<div className="container">
@@ -40,29 +20,70 @@ export function StripeIntegration() {
</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>
{connectedAccountId && (
<h2>Add information to start accepting money</h2>
)}
{stripeConnectInstance && (
<ConnectComponentsProvider connectInstance={stripeConnectInstance}>
<ConnectAccountOnboarding
onExit={() => setOnboardingExited(true)}
/>
</ConnectComponentsProvider>
{connectedAccountId && (
<p>
Matt's Mats partners with Stripe to help you receive payments and
keep your personal bank and details secure.
</p>
)}
{!accountCreatePending && !connectedAccountId && (
<button
onClick={async () => {
setAccountCreatePending(true);
setError(false);
createStripeAccount({}).then((response) => {
const { account_id: accountId } = response;
setAccountCreatePending(false);
if (accountId) {
setConnectedAccountId(accountId);
}
if (error) {
setError(true);
}
});
}}
>
Create an account!
</button>
)}
{connectedAccountId && !accountLinkCreatePending && (
<button
onClick={() => {
setAccountLinkCreatePending(true);
setError(false);
createStripeAccountLink({
stripeAccountId: connectedAccountId,
})
.then((res) => {
const { clientSecret } = res;
setAccountLinkCreatePending(false);
if (clientSecret.url) {
window.location.href = clientSecret.url;
}
})
.catch(() => {
setError(true);
});
}}
>
Add information
</button>
)}
{error && <p className="error">Something went wrong!</p>}
{(connectedAccountId || accountCreatePending || onboardingExited) && (
{(connectedAccountId ||
accountCreatePending ||
accountLinkCreatePending) && (
<div className="dev-callout">
{connectedAccountId && (
<p>
@@ -71,17 +92,14 @@ export function StripeIntegration() {
</p>
)}
{accountCreatePending && <p>Creating a connected account...</p>}
{onboardingExited && (
<p>The Account Onboarding component has exited</p>
)}
{accountLinkCreatePending && <p>Creating a new Account Link...</p>}
</div>
)}
<div className="info-callout">
<p>
This is a sample app for Connect onboarding using the Account
Onboarding embedded component.{' '}
This is a sample app for Stripe-hosted Connect onboarding.{' '}
<a
href="https://docs.stripe.com/connect/onboarding/quickstart?connect-onboarding-surface=embedded"
href="https://docs.stripe.com/connect/onboarding/quickstart?connect-onboarding-surface=hosted"
target="_blank"
rel="noopener noreferrer"
>
@@ -92,4 +110,4 @@ export function StripeIntegration() {
</div>
</div>
);
}
};