Disconnecting from Stripe on the Client
We've managed to create the functionality to have a logged-in user in our app connect with Stripe. In this lesson, we'll look to visually indicate that the user is in the connected state and have an action responsible for allowing the user to disconnect from Stripe.
We've managed to create the functionality to have a logged-in user in our app connect with Stripe. In this lesson, we'll look to visually indicate that the user is in the connected state and have an action responsible for allowing the user to disconnect from Stripe.
User connected with Stripe#
At this moment, when we query for a user
object in the /user/:id
page, we're already querying for the hasWallet
field within the User
graphQL object. The hasWallet
field indicates the presence of a value in the walletId
of the user in our database. If the hasWallet
field is truthy, it means we have the stripe_user_id
of the user.
If we take a look at the <UserProfile />
component we've prepared, we've specified that the additional details section is only shown when a user is logged in. However, the information that encompasses the button to connect with the Stripe should only be shown when the user hasn't yet connected with Stripe.
We'll take the markup that encompasses the button and description to allow a user to connect with Stripe and place it within a constant element we'll call additionalDetails
. We'll say if the hasWallet
field in the user
object available as props is not true, the additionalDetails
constant will contain the "Connect with Stripe"
related markup we've seen before.
We'll also place the additionalDetails
constant element within the additionalDetailsSection
constant element.
// ...
export const UserProfile = ({ user, viewerIsUser }: Props) => {
const additionalDetails = user.hasWallet ? (
<Fragment></Fragment>
) : (
<Fragment>
<Paragraph>
Interested in becoming a TinyHouse host? Register with your Stripe account!
</Paragraph>
<Button
type="primary"
className="user-profile__details-cta"
onClick={redirectToStripe}
>
Connect with Stripe
</Button>
<Paragraph type="secondary">
TinyHouse uses{" "}
<a
href="https://stripe.com/en-US/connect"
target="_blank"
rel="noopener noreferrer"
>
Stripe
</a>{" "}
to help transfer your earnings in a secure and truster manner.
</Paragraph>
</Fragment>
);
const additionalDetailsSection = viewerIsUser ? (
<Fragment>
<Divider />
<div className="user-profile__details">
<Title level={4}>Additional Details</Title>
{additionalDetails}
</div>
</Fragment>
) : null;
// ...
};
We'll now look to prepare the markup in the additionalDetails
constant for when the user is connected with Stripe (i.e. user.hasWallet
is true
). We'll first import one other component from Ant Design that we'll use - the <Tag />
component.
import { Avatar, Button, Card, Divider, Tag, Typography } from "antd";
We'll also import the utility function formatListingPrice()
that is used to format price number values in our client app. We'll import the formatListingPrice()
function from the src/lib/utils/
folder.
import { formatListingPrice } from "../../../../lib/utils";
In the additionalDetails
constant element within the <UserProfile />
component, when the user hasWallet
field is true
, the markup we'll prepare will have:
A green
<Tag />
that says"Stripe Registered"
.A
<Paragraph />
to display the income of the user with which we'll be able to access from theuser.income
field. If theincome
value withinuser
exists, we'll use theformatListingPrice()
function to format the income. Otherwise we'll simply show$0
.A
<Button />
to allow the user to disconnect from Stripe.A secondary
<Paragraph />
that will tell the user that if they were to disconnect from Stripe, this will prevent other users from booking listings that they might have already created.
// ...
export const UserProfile = ({ user, viewerIsUser }: Props) => {
const additionalDetails = user.hasWallet ? (
<Fragment>
<Paragraph>
<Tag color="green">Stripe Registered</Tag>
</Paragraph>
<Paragraph>
Income Earned:{" "}
<Text strong>{user.income ? formatListingPrice(user.income) : `$0`}</Text>
</Paragraph>
<Button type="primary" className="user-profile__details-cta">
Disconnect Stripe
</Button>
<Paragraph type="secondary">
By disconnecting, you won't be able to receive{" "}
<Text strong>any further payments</Text>. This will prevent users from booking
listings that you might have already created.
</Paragraph>
</Fragment>
) : (
<Fragment>
<Paragraph>
Interested in becoming a TinyHouse host? Register with your Stripe account!
</Paragraph>
<Button
type="primary"
className="user-profile__details-cta"
onClick={redirectToStripe}
>
Connect with Stripe
</Button>
<Paragraph type="secondary">
TinyHouse uses{" "}
<a
href="https://stripe.com/en-US/connect"
target="_blank"
rel="noopener noreferrer"
>
Stripe
</a>{" "}
to help transfer your earnings in a secure and truster manner.
</Paragraph>
</Fragment>
);
const additionalDetailsSection = viewerIsUser ? (
<Fragment>
<Divider />
<div className="user-profile__details">
<Title level={4}>Additional Details</Title>
{additionalDetails}
</div>
</Fragment>
) : null;
// ...
};
If we take a look at our user page now and if we're connected with Stripe, we'll see the new section be shown in the user profile section.
disconnectStripe
#
We'll now focus on providing the capability for a user to disconnect from Stripe when connected. We have the disconnectStripe
mutation set up on the server so we'll first create the mutation document in our client.
We'll head to our src/lib/graphql/mutations/
folder and create a DisconnectStripe/
folder that is to have an index.ts
file.
client/
src/
lib/
graphql/
mutations/
// ...
DisconnectStripe/
index.ts
// ...
// ...
// ...
The disconnectStripe
mutation takes no arguments and we'll want the hasWallet
field from the viewer object to be returned.
import { gql } from "apollo-boost";
export const DISCONNECT_STRIPE = gql`
mutation DisconnectStripe {
disconnectStripe {
hasWallet
}
}
`;
In the src/lib/graphql/mutations/index.ts
file, we'll re-export the DISCONNECT_STRIPE
mutation document.
export * from "./DisconnectStripe";
We'll run the codegen:generate
command to autogenerate the TypeScript definitions.
npm run codegen:generate
We'll have the disconnectStripe
mutation be used in the <UserProfile />
component. In the <UserProfile/>
component file, we'll import the useMutation
Hook, the DISCONNECT_STRIPE
mutation document, and the relevant TypeScript definitions for the disconnectStripe
mutation. There are no variables for this mutation so we'll only import the data interface.
import { useMutation } from "@apollo/react-hooks";
// ...
import { DISCONNECT_STRIPE } from "../../../../lib/graphql/mutations/";
import { DisconnectStripe as DisconnectStripeData } from
"../../../../lib/graphql/mutations/DisconnectStripe/__generated__/DisconnectStripe";
import { User as UserData } from "../../../../lib/graphql/queries/User/__generated__/User";
// ...
At the beginning of our <UserProfile />
component function, we'll use the useMutation
Hook, pass along the appropriate type definitions, and destruct the disconnectStripe
mutation function. The only result field we'll use will be the loading
state of our mutation.
// ...
export const UserProfile = ({ user, viewerIsUser }: Props) => {
const [disconnectStripe, { loading }] = useMutation<DisconnectStripeData>(
DISCONNECT_STRIPE
);
// ...
};
To handle the success and error states of the mutation, we'll look to either display a success notification or an error message. Since we're not going to render anything for these conditions, we'll simply handle these conditions with the onCompleted()
and onError()
callback functions as part of the mutation results.
This page is a preview of TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two