diff --git a/.env.example b/.env.example index 88180e53..d49892e3 100644 --- a/.env.example +++ b/.env.example @@ -14,15 +14,4 @@ PORT=3000 SECRET=$NEXTAUTH_SECRET # Database URI. See README.md for instructions on setting up a MongoDB database -MONGO_URI="mongodb://127.0.0.1:27017/pose" - -# Account ID used to demo a working account -EXAMPLE_DEMO_ACCOUNT=acct_1P43pjGaLUbhGD0f - -# Account ID used for the account to demo onboarding -EXAMPLE_DEMO_ONBOARDING_ACCOUNT=acct_1P2GXhGgWcL79Dab - -# Account IDs used for the account to demo onboarding in a foreign country -EXAMPLE_DEMO_ONBOARDING_ACCOUNT_FR=acct_1P4Z4z2fnsbO0Avm -EXAMPLE_DEMO_ONBOARDING_ACCOUNT_SG=acct_1P5rkB2fWq8wiVXn -EXAMPLE_DEMO_ONBOARDING_ACCOUNT_HK=acct_1P5sFXGbiGbAYBjx +MONGO_URI="mongodb://127.0.0.1:27017/pose" \ No newline at end of file diff --git a/.node-version b/.node-version index 3c5535cf..a05e3d4f 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -18.19.1 +18.20.2 \ No newline at end of file diff --git a/README.md b/README.md index a7aebcb6..d3a65824 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# FurEver: Stripe Connect embedded components demo +# FurEver: Stripe Connect embedded components integration tutorial FurEver is a vertical SaaS grooming platform for pet salons to manage their e2e business operations. FurEver wants to provide access to Stripe products and UIs directly in their website, at a fraction of the engineering cost, using [Stripe Connect](https://stripe.com/connect) and [Stripe Connect embedded components](https://docs.stripe.com/connect/get-started-connect-embedded-components). -**See a live version on [furever.dev](https://furever.dev).** +**See a live version with embedded components on [furever.dev](https://furever.dev).** @@ -17,24 +17,6 @@ FurEver showcases the integration between a platform's website, [Stripe Connect] The user will then onboard with Stripe via embedded onboarding. Thereafter, Connect embedded components will provide the UI surfaces for account management and dashboard UI elements with just a few lines of code. The demo website also uses the Stripe API to create test payments and payouts. This app also contains a basic authentication system. -FurEver makes use of the following [Connect embedded components](https://docs.stripe.com/connect/supported-embedded-components): - -- `` enables an embedded onboarding experience without redirecting users to Stripe hosted onboarding. -- `` provides a list to display Stripe payments, refunds, and disputes. This also includes handling list filtering, pagination, and CSV exports. -- `` provides a list to display Stripe payouts and balance. This also includes handling list filtering, pagination, and CSV exports. -- `` allows users to edit their Stripe account settings without navigating to the Stripe dashboard. -- `` displays a list of current and future risk requirements an account needs to resolve. -- `` displays a list of tax invoice documents. -- `` allows users to [set up Stripe Tax](https://docs.stripe.com/tax/set-up). -- `` allows users to control their tax compliance settings. - -Additionally, the following preview components are also used: - -- `` **preview** allows users to check their eligibility for financing, get an overview of their in-progress financing, and access the reporting page to review paydown transactions. -- `` **preview** renders a view of an individual [Financial Account](https://docs.stripe.com/api/treasury/financial_accounts) -- `` **preview** provides a list of transactions associated with a financial account. -- `` **preview** provides a list of all the cards issued. - ### Architecture The web application is implemented as as full-stack application using Express, React, Typescript, and Material UI. @@ -50,21 +32,29 @@ To integrate Stripe Connect embedded components, check out our [documentation](h 1. [`hooks/useConnect.ts`](client/hooks/Connect.tsx) shows the client side integration with Connect embedded components. 2. [`api/account_session/route.ts`](server/routes/stripe.ts) shows the server request to `v1/account_sessions`. -## Requirements -You'll need a Stripe account to manage pet salon onboarding and payments: +## Getting started -- [Sign up for free](https://dashboard.stripe.com/register), then [enable Connect](https://dashboard.stripe.com/account/applications/settings) by filling in your Connect settings. -- Fill in the necessary information in the **Branding** section in [Connect settings](https://dashboard.stripe.com/test/settings/connect). +(Optional) Install Node and Yarn if you need to: -### Getting started +``` +brew install nodenv +nodenv install 18.20.2 +brew install yarn +``` -Install dependencies using npm (or yarn): +Install dependencies using yarn (or npm): ``` yarn ``` +(Optional) Create a new Stripe account to manage pet salon onboarding and payments: + +- [Sign up for free](https://dashboard.stripe.com/register) +- [Enable Connect](https://dashboard.stripe.com/account/applications/settings) by filling in your Connect settings. + + Copy the environment file and add your own [Stripe API keys](https://dashboard.stripe.com/account/apikeys): ``` @@ -86,3 +76,156 @@ yarn dev ``` Go to `http://localhost:{process.env.PORT}` in your browser to start using the app. + +## Add Connect embedded components +### Connect embedded components session + +- Create an account session in app/api/account_session/route.ts + +```js +// Create an account session with embedded components +const accountSession = await stripe.accountSessions.create({ + account: stripeAccountId, + components: { + account_onboarding: { + enabled: true, + features: { + // Disable the authenticate user step + disable_stripe_user_authentication: true, + }, + }, + payments: { + enabled: true, + features: { + // Enable dispute and refund management + dispute_management: true, + refund_management: true, + }, + }, + payouts: { + enabled: true, + features: { + // Disable the authenticate user step + disable_stripe_user_authentication: true, + }, + }, + }, +}); +return new Response(JSON.stringify(accountSession), { + status: 200, + headers: {'Content-Type': 'application/json'}, +}); +``` + +### Connect Onboarding component +- Load StripeConnectInstance in app/(auth)/onboarding/page.tsx +```js +// Fetch StripeConnectInstance to create embedded components +return loadConnectAndInitialize({ + publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY!, + fetchClientSecret: fetchClientSecret, + appearance: { + overlays: 'dialog', + variables: { + colorPrimary: '#625afa', + }, + }, +}); +``` + +- Add Connect Account Onboarding component in app/(auth)/onboarding/page.tsx +```js +return ( + + { + window.location.href = '/home?shownux=true'; + }} + /> + +); +``` + +- Onboard new salon + +### Connect Payments component +- Create test payments in the application + +- Load StripeConnectInstance in app/(dashboard)/payments/page.tsx +```js +// Fetch StripeConnectInstance to create embedded components +const [stripeConnectInstance] = React.useState(() => { + const fetchClientSecret = async () => { + // Fetch the AccountSession client secret + const response = await fetch('/api/account_session', {method: 'POST'}); + if (!response.ok) { + // Handle errors on the client side here + const {error} = await response.json(); + console.log('An error occurred: ', error); + return undefined; + } else { + const {client_secret: clientSecret} = await response.json(); + return clientSecret; + } + }; + return loadConnectAndInitialize({ + publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY!, + fetchClientSecret: fetchClientSecret, + appearance: { + overlays: 'dialog', + variables: { + colorPrimary: '#625afa', + }, + }, + }); +}); +``` + +- Add Connect Payments component in app/(dashboard)/payments/page.tsx +```js + + + +``` + +- Test Payments component with successful, declined and disputed payments + +### Connect Payouts component +- Create test payout in the application +- Load StripeConnectInstance in app/(dashboard)/payouts/page.tsx +```js +// Fetch StripeConnectInstance to create embedded components +const [stripeConnectInstance] = React.useState(() => { + const fetchClientSecret = async () => { + // Fetch the AccountSession client secret + const response = await fetch('/api/account_session', {method: 'POST'}); + if (!response.ok) { + // Handle errors on the client side here + const {error} = await response.json(); + console.log('An error occurred: ', error); + return undefined; + } else { + const {client_secret: clientSecret} = await response.json(); + return clientSecret; + } + }; + return loadConnectAndInitialize({ + publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY!, + fetchClientSecret: fetchClientSecret, + appearance: { + overlays: 'dialog', + variables: { + colorPrimary: '#625afa', + }, + }, + }); +}); +``` + +- Add Connect Payouts component in app/(dashboard)/payouts/page.tsx +```js + + + +``` +- Test Payouts component in the application \ No newline at end of file diff --git a/app/(auth)/business/form.tsx b/app/(auth)/business/form.tsx index 396fd7bd..4982fbe7 100644 --- a/app/(auth)/business/form.tsx +++ b/app/(auth)/business/form.tsx @@ -434,90 +434,7 @@ export default function BusinessDetailsForm({email}: {email: string}) { )} /> - - - -

- These options allow you to configure your connected account's - controller properties.{' '} - - Learn more - -

-
- ( - <> - - Stripe dashboard access - - - - - - )} - /> -
-
- ( - <> - - Negative balance liability - - - - - - )} - /> -
-
- ( - <> - - Stripe fee collection - - - - - - )} - /> -
-
- - )} - - ); -} diff --git a/app/(dashboard)/home/page.tsx b/app/(dashboard)/home/page.tsx index 15de7c0b..3a614374 100644 --- a/app/(dashboard)/home/page.tsx +++ b/app/(dashboard)/home/page.tsx @@ -1,13 +1,10 @@ 'use client'; import React from 'react'; -import {useState} from 'react'; import Schedule from '@/app/components/Schedule'; import BalanceWidget from '@/app/components/BalanceWidget'; import RecentPaymentsWidget from '@/app/components/RecentPaymentsWidget'; import MonthToDateWidget from '@/app/components/MonthToDateWidget'; import CustomersWidget from '@/app/components/CustomersWidget'; -import EmbeddedComponentContainer from '@/app/components/EmbeddedComponentContainer'; -import {ConnectNotificationBanner} from '@stripe/react-connect-js'; import {useSession} from 'next-auth/react'; import {redirect} from 'next/navigation'; import Container from '@/app/components/Container'; @@ -20,34 +17,9 @@ export default function Dashboard() { const name = session.user.stripeAccount.individual?.first_name; - const BREAKPOINT = 1190; - - const [showBanner, setShowBanner] = React.useState(false); - - const renderConditionallyCallback = (response: { - total: number; - actionRequired: number; - }) => { - if (response && response.total > 0) { - setShowBanner(true); - } else { - setShowBanner(false); - } - }; - return ( <>

Woof woof, {name || 'human'}!

-
- - - -
diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index 05f59b17..4d9a386d 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -1,18 +1,10 @@ 'use client'; import AuthenticatedAndOnboardedRoute from '@/app/components/AuthenticatedAndOnboardedRoute'; -import Nav from '@/app/components/Nav'; -import Container from '@/app/components/Container'; -import { - useToolsContext, - ToolsPanelProvider, -} from '@/app/hooks/ToolsPanelProvider'; -import {EmbeddedComponentWrapper} from '@/app/hooks/EmbeddedComponentWrapper'; -import OnboardingDialog from '../components/OnboardingDialog'; +import {ToolsPanelProvider} from '@/app/hooks/ToolsPanelProvider'; import DataRequest from '../components/DataRequest'; import Screen from '../components/Screen'; import * as React from 'react'; -import {useSettings} from '../hooks/useSettings'; export default function DashboardLayout({ children, @@ -21,13 +13,11 @@ export default function DashboardLayout({ }>) { return ( - - - - {children} - - - + + + {children} + + ); } diff --git a/app/(dashboard)/payments/page.tsx b/app/(dashboard)/payments/page.tsx index e1da90fd..3ab1d0d3 100644 --- a/app/(dashboard)/payments/page.tsx +++ b/app/(dashboard)/payments/page.tsx @@ -1,23 +1,20 @@ 'use client'; import * as React from 'react'; -import {ConnectPayments} from '@stripe/react-connect-js'; import Container from '@/app/components/Container'; -import EmbeddedComponentContainer from '@/app/components/EmbeddedComponentContainer'; import MonthToDateWidget from '@/app/components/MonthToDateWidget'; import CustomersWidget from '@/app/components/CustomersWidget'; -import {Button} from '@/components/ui/button'; -import {LoaderCircle, Plus} from 'lucide-react'; import {useSession} from 'next-auth/react'; -import CreatePaymentsButton from '@/app/components/testdata/CreatePaymentsButton'; +import {useGetCharges} from '@/app/hooks/useGetCharges'; +import { + ConnectComponentsProvider, + ConnectPayments, +} from '@stripe/react-connect-js'; +import {loadConnectAndInitialize} from '@stripe/connect-js'; export default function Payments() { - const {data: session} = useSession(); - const [loading, setLoading] = React.useState(true); - React.useEffect(() => { - setLoading(!session?.user.setup); - }, [session?.user.setup]); + //TODO Fetch StripeConnectInstance to create embedded components return ( <> @@ -34,19 +31,7 @@ export default function Payments() {

Recent payments

- - {loading ? ( -
- - Creating test data -
- ) : ( - - )} -
+

TODO: Add Payments component here!

); diff --git a/app/(dashboard)/payouts/page.tsx b/app/(dashboard)/payouts/page.tsx index f2566efe..d4f7728d 100644 --- a/app/(dashboard)/payouts/page.tsx +++ b/app/(dashboard)/payouts/page.tsx @@ -1,21 +1,17 @@ 'use client'; -import {ConnectPayouts} from '@stripe/react-connect-js'; import Container from '@/app/components/Container'; -import EmbeddedComponentContainer from '@/app/components/EmbeddedComponentContainer'; -import {Button} from '@/components/ui/button'; -import {LoaderCircle, Plus} from 'lucide-react'; import {useSession} from 'next-auth/react'; import React from 'react'; -import CreatePayoutsButton from '@/app/components/testdata/CreatePayoutsButton'; +import {loadConnectAndInitialize} from '@stripe/connect-js'; +import { + ConnectComponentsProvider, + ConnectPayouts, +} from '@stripe/react-connect-js'; export default function Payouts() { - const {data: session} = useSession(); - const [loading, setLoading] = React.useState(true); - React.useEffect(() => { - setLoading(!session?.user.setup); - }, [session?.user.setup]); + //TODO Fetch StripeConnectInstance to create embedded components return ( <> @@ -24,19 +20,7 @@ export default function Payouts() {

Recent payouts

- - {loading ? ( -
- - Creating test data -
- ) : ( - - )} -
+

TODO: Add Payouts component here!

); diff --git a/app/(dashboard)/settings/documents/page.tsx b/app/(dashboard)/settings/documents/page.tsx deleted file mode 100644 index eba4f385..00000000 --- a/app/(dashboard)/settings/documents/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -'use client'; - -import Container from '@/app/components/Container'; -import EmbeddedComponentContainer from '@/app/components/EmbeddedComponentContainer'; -import {ConnectDocuments} from '@stripe/react-connect-js'; - -export default function Documents() { - return ( - -
-

Documents

-

- Access documents and account statements. -

-
- - - -
- ); -} diff --git a/app/(dashboard)/settings/layout.tsx b/app/(dashboard)/settings/layout.tsx index 2a312ade..f9dda0a1 100644 --- a/app/(dashboard)/settings/layout.tsx +++ b/app/(dashboard)/settings/layout.tsx @@ -3,15 +3,12 @@ import {signOut} from 'next-auth/react'; import SubNav from '@/app/components/SubNav'; import {Button} from '@/components/ui/button'; -import {Avatar, AvatarFallback, AvatarImage} from '@/components/ui/avatar'; -import {useConnectJSContext} from '@/app/hooks/EmbeddedComponentProvider'; export default function SettingsLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - const connectJSContext = useConnectJSContext(); return ( <>
@@ -21,18 +18,13 @@ export default function SettingsLayout({
- + {/*
@@ -93,21 +63,9 @@ export default function Settings() {
- - - +

TODO: account management goes here

-
- - -
-

Payment methods

-

Add and manage your payment methods.

-
- - - -
+
*/} ); } diff --git a/app/(dashboard)/settings/tax/page.tsx b/app/(dashboard)/settings/tax/page.tsx deleted file mode 100644 index 00ec950a..00000000 --- a/app/(dashboard)/settings/tax/page.tsx +++ /dev/null @@ -1,34 +0,0 @@ -'use client'; - -import Container from '@/app/components/Container'; -import EmbeddedComponentContainer from '@/app/components/EmbeddedComponentContainer'; -import { - ConnectTaxSettings, - ConnectTaxRegistrations, -} from '@stripe/react-connect-js'; - -export default function Tax() { - return ( - <> - -

Tax

-

- Configure these settings to automatically calculate and collect tax on - your payments. -

- - - -
- -

Tax registrations

-

- Locations where you have a registration, and want to collect taxes. -

- - - -
- - ); -} diff --git a/app/api/account_link/route.ts b/app/api/account_link/route.ts new file mode 100644 index 00000000..12ac7a78 --- /dev/null +++ b/app/api/account_link/route.ts @@ -0,0 +1,40 @@ +import {type NextRequest} from 'next/server'; +import {getServerSession} from 'next-auth/next'; +import {authOptions} from '@/lib/auth'; +import {stripe} from '@/lib/stripe'; + +export async function POST(req: NextRequest) { + try { + const session = await getServerSession(authOptions); + + let stripeAccountId = session?.user?.stripeAccount?.id; + + if (!stripeAccountId) { + return new Response( + JSON.stringify({ + error: 'No Stripe account found for this user', + }), + {status: 400} + ); + } + + // Create an onboarding account link for the connected account + const accountLink = await stripe.accountLinks.create({ + account: stripeAccountId, + type: 'account_onboarding', + refresh_url: `${process.env.NEXTAUTH_URL}/onboarding`, + return_url: `${process.env.NEXTAUTH_URL}/home?shownux=true`, + }); + + return new Response(JSON.stringify(accountLink), { + status: 200, + headers: {'Content-Type': 'application/json'}, + }); + } catch (error: any) { + console.error( + 'An error occurred when calling the Stripe API to create an account link', + error + ); + return new Response(JSON.stringify({error: error.message}), {status: 500}); + } +} diff --git a/app/api/account_session/route.ts b/app/api/account_session/route.ts index 2a822b5b..876b504f 100644 --- a/app/api/account_session/route.ts +++ b/app/api/account_session/route.ts @@ -2,49 +2,10 @@ import {type NextRequest} from 'next/server'; import {getServerSession} from 'next-auth/next'; import {authOptions} from '@/lib/auth'; import {stripe} from '@/lib/stripe'; - export async function POST(req: NextRequest) { try { const session = await getServerSession(authOptions); - - const json = await req.json(); - - const {demoOnboarding, locale} = json; - let stripeAccountId = session?.user?.stripeAccount?.id; - - if (demoOnboarding !== undefined) { - const accountId: string = (() => { - switch (locale) { - case 'fr-FR': - return process.env.EXAMPLE_DEMO_ONBOARDING_ACCOUNT_FR!; - case 'en-SG': - // This doesn't actually have a locale. So this can never be hit - return process.env.EXAMPLE_DEMO_ONBOARDING_ACCOUNT_SG!; - case 'en-GB': - // Use GB english for Hong Kong so fall through - case 'zh-Hant-HK': - return process.env.EXAMPLE_DEMO_ONBOARDING_ACCOUNT_HK!; - default: - // Ignore - return process.env.EXAMPLE_DEMO_ONBOARDING_ACCOUNT!; - } - })(); - - console.log( - `Looking for the demo onboarding account ${accountId} for locale ${locale}` - ); - const demoOnboardingAccount = await stripe.accounts.retrieve(accountId); - if (demoOnboardingAccount) { - console.log( - `Using demo onboarding account: ${demoOnboardingAccount.id}` - ); - stripeAccountId = demoOnboardingAccount.id; - } else { - console.log('No demo onboarding account found'); - } - } - if (!stripeAccountId) { return new Response( JSON.stringify({ @@ -53,98 +14,9 @@ export async function POST(req: NextRequest) { {status: 400} ); } + + //TODO Create an Account Session for embedded components - const account = await stripe.accounts.retrieve(stripeAccountId); - - const isCustom = - account?.controller?.stripe_dashboard?.type === 'none' && - account?.controller?.losses?.payments === 'application' && - account?.controller?.requirement_collection === 'application'; - - // We can only request the components if the account has both issuing and treasury capabilities - const hasIssuingAndTreasury = ['card_issuing', 'treasury'].every( - (capability) => - Object.keys(account?.capabilities || []).includes(capability) - ); - const issuingAndTreasuryComponents = { - issuing_card: { - enabled: true, - }, - issuing_cards_list: { - enabled: true, - features: { - card_management: true, - cardholder_management: true, - }, - }, - financial_account: { - enabled: true, - features: { - money_movement: true, - external_account_collection: !isCustom, - }, - }, - financial_account_transactions: { - enabled: true, - features: { - card_spend_dispute_management: true, - }, - }, - }; - - const accountSession = await stripe.accountSessions.create({ - account: stripeAccountId, - components: { - // Payments - payments: { - enabled: true, - }, - payouts: { - enabled: true, - features: { - instant_payouts: true, - standard_payouts: true, - edit_payout_schedule: true, - external_account_collection: !isCustom, - }, - }, - // Connect - account_management: { - enabled: true, - features: {external_account_collection: !isCustom}, - }, - account_onboarding: { - enabled: true, - features: {external_account_collection: !isCustom}, - }, - // @ts-ignore - payment_method_settings: {enabled: true}, - documents: {enabled: true}, - notification_banner: { - enabled: true, - features: {external_account_collection: !isCustom}, - }, - capital_overview: { - enabled: true, - }, - ...(hasIssuingAndTreasury ? issuingAndTreasuryComponents : {}), - // @ts-ignore - tax_settings: { - enabled: true, - }, - // @ts-ignore - tax_registrations: { - enabled: true, - }, - }, - }); - - return new Response( - JSON.stringify({ - client_secret: accountSession.client_secret, - }), - {status: 200, headers: {'Content-Type': 'application/json'}} - ); } catch (error: any) { console.error( 'An error occurred when calling the Stripe API to create an account session', @@ -152,4 +24,4 @@ export async function POST(req: NextRequest) { ); return new Response(JSON.stringify({error: error.message}), {status: 500}); } -} +} \ No newline at end of file diff --git a/app/api/charges/route.ts b/app/api/charges/route.ts new file mode 100644 index 00000000..63d20657 --- /dev/null +++ b/app/api/charges/route.ts @@ -0,0 +1,40 @@ +import {type NextRequest} from 'next/server'; +import {getServerSession} from 'next-auth/next'; +import {authOptions} from '@/lib/auth'; +import {stripe} from '@/lib/stripe'; + +export async function GET(req: NextRequest) { + try { + const session = await getServerSession(authOptions); + + let stripeAccountId = session?.user?.stripeAccount?.id; + + if (!stripeAccountId) { + return new Response( + JSON.stringify({ + error: 'No Stripe account found for this user', + }), + {status: 400} + ); + } + + // Get all charges + const charges = await stripe.charges.list( + {}, + { + stripeAccount: stripeAccountId, + } + ); + + return new Response(JSON.stringify(charges.data), { + status: 200, + headers: {'Content-Type': 'application/json'}, + }); + } catch (error: any) { + console.error( + 'An error occurred when calling the Stripe API to list charges', + error + ); + return new Response(JSON.stringify({error: error.message}), {status: 500}); + } +} diff --git a/app/api/setup_accounts/create_charges/route.ts b/app/api/setup_accounts/create_charges/route.ts index 23e318bc..e81173b6 100644 --- a/app/api/setup_accounts/create_charges/route.ts +++ b/app/api/setup_accounts/create_charges/route.ts @@ -108,7 +108,8 @@ const createPaymentIntentForNonCardPayments = async ( payment_method: paymentMethod.id, description, customer: customerId, - statement_descriptor: process.env.APP_NAME, + // statement_descriptor: process.env.APP_NAME, + receipt_email: 'test+test@stripe.com', confirmation_method: 'manual', confirm: true, payment_method_types: ['us_bank_account'], @@ -158,7 +159,8 @@ const createPaymentIntentForNonCardPayments = async ( payment_method: paymentMethod.id, description, customer: customerId, - statement_descriptor: process.env.APP_NAME, + // statement_descriptor: process.env.APP_NAME, + receipt_email: 'test+test@stripe.com', confirmation_method: 'manual', confirm: true, payment_method_types: ['sepa_debit'], @@ -230,7 +232,8 @@ export async function POST(req: NextRequest) { payment_method_types: ['card'], description, customer: metadata.customerId, - statement_descriptor: process.env.APP_NAME, + // statement_descriptor: process.env.APP_NAME, + receipt_email: 'test+test@stripe.com', confirmation_method: 'manual', confirm: true, ...(status === 'card_uncaptured' diff --git a/app/components/EmbeddedComponentContainer.tsx b/app/components/EmbeddedComponentContainer.tsx deleted file mode 100644 index a2b183e1..00000000 --- a/app/components/EmbeddedComponentContainer.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import {useEmbeddedComponentBorder} from '@/app/hooks/EmbeddedComponentBorderProvider'; -import {ChevronRight} from 'lucide-react'; - -const EmbeddedComponentContainer = ({ - children, - className, - componentName, -}: { - children: React.ReactNode; - className?: string; - componentName: string; -}) => { - const {enableBorder} = useEmbeddedComponentBorder(); - - const ComponentDetails = () => { - const ComponentURLs: {[key: string]: any} = { - AccountManagement: - 'https://docs.stripe.com/connect/supported-embedded-components/account-management', - AccountOnboarding: - 'https://docs.stripe.com/connect/supported-embedded-components/account-onboarding', - Balances: - 'https://docs.stripe.com/connect/supported-embedded-components/balances', - CapitalOverview: - 'https://docs.stripe.com/connect/supported-embedded-components/capital-overview', - Documents: - 'https://docs.stripe.com/connect/supported-embedded-components/documents', - FinancialAccount: - 'https://docs.stripe.com/connect/supported-embedded-components/financial-account', - FinancialAccountTransactions: - 'https://docs.stripe.com/connect/supported-embedded-components/financial-account-transactions', - InstantPayouts: - 'https://docs.stripe.com/connect/supported-embedded-components/instant-payouts', - IssuingCard: - 'https://docs.stripe.com/connect/supported-embedded-components/issuing-card', - IssuingCardsList: - 'https://docs.stripe.com/connect/supported-embedded-components/issuing-cards-list', - NotificationBanner: - 'https://docs.stripe.com/connect/supported-embedded-components/notification-banner', - Payments: - 'https://docs.stripe.com/connect/supported-embedded-components/payments', - Payouts: - 'https://docs.stripe.com/connect/supported-embedded-components/payouts', - PayoutsList: - 'https://docs.stripe.com/connect/supported-embedded-components/payouts-list', - PaymentMethodSettings: - 'https://docs.stripe.com/connect/supported-embedded-components/payment-method-settings', - TaxRegistrations: - 'https://docs.stripe.com/connect/supported-embedded-components/tax-registrations', - TaxSettings: - 'https://docs.stripe.com/connect/supported-embedded-components/tax-settings', - }; - - if (!enableBorder) { - return; - } - - if (componentName in ComponentURLs === false) { - // If component name is not found, don't show the link - return; - } - - return ( - - ); - }; - - return ( -
- - {children} -
- ); -}; - -export default EmbeddedComponentContainer; diff --git a/app/components/Nav.tsx b/app/components/Nav.tsx index 39db690f..d6c65c57 100644 --- a/app/components/Nav.tsx +++ b/app/components/Nav.tsx @@ -47,21 +47,10 @@ const navigationMenuItems = [ icon: CoinsIcon, paths: [], }, - { - label: 'Finances', - href: '/finances', - icon: LandmarkIcon, - paths: ['/finances/cards'], - shouldDisplayFilter: (stripeAccount: Stripe.Account) => - stripeAccount.controller?.stripe_dashboard?.type === 'none' && - stripeAccount.controller?.losses?.payments === 'application' && - stripeAccount.controller?.requirement_collection === 'application', - }, { label: 'Account', href: '/settings', icon: SettingsIcon, - paths: ['/settings/documents', '/settings/tax'], }, ]; @@ -101,41 +90,32 @@ const Nav = () => { className={`${showMobileNavItems ? 'flex' : 'hidden'} w-full flex-1 p-2 pb-3 shadow-xl transition sm:flex sm:p-0 sm:shadow-none`} >
    - {navigationMenuItems - .filter(({shouldDisplayFilter}) => { - // Not all pages require a filter. - if (!shouldDisplayFilter || !stripeAccount) { - return true; - } - - return shouldDisplayFilter(stripeAccount); - }) - .map((item) => ( -
  • - - - -
  • - ))} + />{' '} + {item.label} + + + + ))}
= [ - {label: 'Dialog', overlayType: 'dialog'}, - {label: 'Drawer', overlayType: 'drawer'}, -]; - -const OverlaySelector = () => { - const settings = useContext(SettingsContext); - - const setOverlay = useCallback( - (value: OverlayOption) => { - settings.handleUpdate({overlay: value}); - }, - [settings] - ); - - const overlay = - Overlays.find((o) => o.overlayType === settings.overlay) || Overlays[0]!; - - return ( - - ); -}; - -export default OverlaySelector; diff --git a/app/components/ToolsPanel.tsx b/app/components/ToolsPanel.tsx index 51edd149..c2f93f00 100644 --- a/app/components/ToolsPanel.tsx +++ b/app/components/ToolsPanel.tsx @@ -2,7 +2,6 @@ import {useSession} from 'next-auth/react'; import Image from 'next/image'; -import Link from 'next/link'; import {usePathname} from 'next/navigation'; import Stripe from '@/public/stripe-gray.svg'; import { @@ -19,25 +18,13 @@ import { X, } from 'lucide-react'; import {Button} from '@/components/ui/button'; -import FureverLogo from '@/public/furever_logo.png'; -import {Switch} from '@/components/ui/switch'; import {Label} from '@/components/ui/label'; -import { - Select, - SelectTrigger, - SelectContent, - SelectItem, - SelectValue, -} from '@/components/ui/select'; -import {RadioGroup, RadioGroupItem} from '@/components/ui/radiogroup'; import {Sparkles} from 'lucide-react'; -import {useEmbeddedComponentBorder} from '../hooks/EmbeddedComponentBorderProvider'; import {useToolsContext} from '../hooks/ToolsPanelProvider'; import * as React from 'react'; import CreatePaymentsButton from './testdata/CreatePaymentsButton'; import LocaleSelector from './Tools/LocaleSelector'; import ThemePicker from './Tools/ThemePicker'; -import OverlaySelector from './Tools/OverlaySelector'; import CreateInterventionsButton from './testdata/CreateInterventionsButton'; import CreatePayoutsButton from './testdata/CreatePayoutsButton'; import CreateFinancialCreditButton from './testdata/CreateFinancialCreditButton'; @@ -50,7 +37,6 @@ const ToolsPanel = () => { const stripeAccount = session?.user?.stripeAccount; const [showMobileNavItems, setShowMobileNavItems] = React.useState(false); - const {handleEnableBorderChange, enableBorder} = useEmbeddedComponentBorder(); const {handleOpenChange} = useToolsContext(); const [border, setBorder] = React.useState(true); const [theme, setTheme] = React.useState('light'); @@ -118,17 +104,6 @@ const ToolsPanel = () => { const DefaultTools = () => { return (
-
- - handleEnableBorderChange(!border)} - /> -