Created
September 15, 2022 18:45
-
-
Save RichardSPrins/1e608f3906b0f5946f4994a9d3fbbe1f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from "react"; | |
import { GetServerSidePropsContext } from "next"; | |
import { unstable_getServerSession } from "next-auth"; | |
import AppLayout from "../../layouts/AppLayout"; | |
import { trpc } from "../../utils/trpc"; | |
import { authOptions } from "../api/auth/[...nextauth]"; | |
import { | |
Button, | |
CopyButton, | |
Tooltip, | |
ActionIcon, | |
Input, | |
Popover, | |
} from "@mantine/core"; | |
import { useDisclosure } from "@mantine/hooks"; | |
import { HiOutlineClipboardCopy, HiCheck } from "react-icons/hi"; | |
import { IoMdHelpCircleOutline } from "react-icons/io"; | |
import useQueryParams from "../../hooks/useQueryParams"; | |
import { ParsedUrlQuery } from "querystring"; | |
export default function AppSettingsPage() { | |
const queryParams = useQueryParams(); | |
const [stripedata, setStripeData] = React.useState(null); | |
const [opened, { close, open }] = useDisclosure(false); | |
// const { data: config, status } = trpc.useQuery(["auth.getAuthUserConfig"]); | |
// const { data: currentUser, status: userStatus } = trpc.useQuery([ | |
// "auth.getAuthUser", | |
// ]); | |
const { | |
data: stripeConnect, | |
error: stripeConnectError, | |
isLoading: stripeConnectLoading, | |
mutate: mutateStripeConnect, | |
} = trpc.useMutation(["stripe.connectStripe"]); | |
const [newDonationLink, setNewDonationLink] = React.useState<string | null>( | |
null | |
); | |
const [loadingNewLink, setLoadingNewLink] = React.useState<boolean>(false); | |
const handleGenerateNewLink = () => { | |
setLoadingNewLink(true); | |
setTimeout(() => { | |
setLoadingNewLink(false); | |
setNewDonationLink( | |
`${window.location.origin}/${Math.floor( | |
Math.random() * 99999999999 | |
)}/donate` | |
); | |
}, 2000); | |
}; | |
// React.useEffect(() => { | |
// if (!queryParams) { | |
// return; | |
// } | |
// if ( | |
// Object.keys(queryParams).includes("scope") && | |
// Object.keys(queryParams).includes("code") | |
// ) { | |
// handleStripeConnect(queryParams); | |
// console.log({ queryParams }); | |
// } | |
// }, [queryParams]); | |
// const handleStripeConnect = async (queryParams: ParsedUrlQuery) => { | |
// const { scope, code } = queryParams; | |
// await mutateStripeConnect({ | |
// scope: scope as string, | |
// code: code as string, | |
// }); | |
// // setStripeData(result) | |
// console.log({ stripeConnect }); | |
// }; | |
return ( | |
<AppLayout> | |
<p className="text-4xl font-bold">Account Settings</p> | |
<div className="max-w-4xl mx-auto mt-12 flex flex-col"> | |
<div className="flex justify-end"> | |
<Button color="green">Save Changes</Button> | |
</div> | |
<div className="mb-4 w-full"> | |
<label | |
className="block text-gray-700 text-sm font-bold mb-1" | |
htmlFor="displayName" | |
> | |
Display Name | |
</label> | |
<Input placeholder="Your email" sx={{ maxWidth: "350px" }} /> | |
<p className="text-xs text-slate-500 mt-2"> | |
* This is necessary if you want to use a personalized name for your | |
donation link | |
</p> | |
</div> | |
<div className="mb-4 w-full"> | |
<p className="block text-gray-700 text-sm font-bold mb-1"> | |
Donation Link | |
</p> | |
{!newDonationLink && ( | |
<p className="text-xs text-slate-500 my-3"> | |
You do not have a donation link yet. Create one to start earning! | |
</p> | |
)} | |
{newDonationLink && ( | |
<div className="flex gap-2 items-center my-2"> | |
<span className="text-sm text-gray-700 bg-gray-100 px-2 rounded-sm"> | |
{newDonationLink} | |
</span> | |
<CopyButton value={newDonationLink}> | |
{({ copied, copy }) => ( | |
<Tooltip | |
label={copied ? "Copied" : "Copy"} | |
withArrow | |
position="top" | |
> | |
<ActionIcon color={copied ? "teal" : "gray"} onClick={copy}> | |
{copied ? <HiCheck /> : <HiOutlineClipboardCopy />} | |
</ActionIcon> | |
</Tooltip> | |
)} | |
</CopyButton> | |
</div> | |
)} | |
<Button | |
color="dark" | |
loading={loadingNewLink} | |
onClick={handleGenerateNewLink} | |
> | |
Generate New Link | |
</Button> | |
</div> | |
<div className="mb-4 w-full"> | |
<div className="flex gap-1 items-center mb-1"> | |
<p className="block text-gray-700 text-sm font-bold"> | |
Stripe Account | |
</p> | |
</div> | |
<p className="text-xs text-slate-500 my-2"> | |
* This is necessary to collect donation payments. Please connect a | |
Stripe account to use our services. | |
</p> | |
<Button | |
color={"blue"} | |
onClick={() => { | |
if (window) { | |
const url = `https://dashboard.stripe.com/oauth/authorize?response_type=code&client_id=${process.env.NEXT_PUBLIC_STRIPE_OAUTH_CLIENT_ID}&scope=read_write&redirect_uri=${process.env.NEXT_PUBLIC_BASE_URL}/app/account`; | |
window.document.location.href = url; | |
} | |
}} | |
> | |
Connect Stripe | |
</Button> | |
</div> | |
{/* <pre>{stripeData && JSON.stringify(stripeData, null, 2)}</pre> */} | |
</div> | |
</AppLayout> | |
); | |
} | |
export async function getServerSideProps(context: GetServerSidePropsContext) { | |
const session = await unstable_getServerSession( | |
context.req, | |
context.res, | |
authOptions | |
); | |
if (!session) { | |
return { | |
redirect: { destination: "/sign-in" }, | |
}; | |
} | |
return { | |
props: {}, | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { TRPCError } from "@trpc/server"; | |
import { z } from "zod"; | |
import { createProtectedRouter } from "./context"; | |
export const stripeRouter = createProtectedRouter() | |
.middleware(async ({ ctx, next }) => { | |
// Any queries or mutations after this middleware will | |
// raise an error unless there is a current session | |
if (!ctx.session) { | |
throw new TRPCError({ code: "UNAUTHORIZED" }); | |
} | |
return next(); | |
}) | |
.query("getBalance", { | |
resolve({ ctx }) { | |
// TODO: replace with Stripe Connected Account balance query result | |
// Set your secret key. Remember to switch to your live secret key in production. | |
// See your keys here: https://dashboard.stripe.com/apikeys | |
// const balance = await stripe.balance.retrieve({ | |
// stripeAccount: '{{CONNECTED_STRIPE_ACCOUNT_ID}}' | |
// }); | |
return 100; | |
}, | |
}) | |
.mutation("connectStripe", { | |
input: z.object({ scope: z.string(), code: z.string() }), | |
async resolve({ ctx, input }) { | |
try { | |
const { scope, code } = input; | |
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); | |
const result = await stripe.oauth | |
.token({ | |
grant_type: "authorization_code", | |
code: code, | |
}) | |
.catch((err: any) => { | |
throw new Error("Stripe oauth fail", err.message); | |
}); | |
const account = await stripe.accounts | |
?.retrieve(result?.stripe_user_id) | |
?.catch((err: any) => { | |
throw new Error("Error fetching stripe account", err.message); | |
}); | |
// Here we get the important details of the account. | |
const accountAnalysis = { | |
hasConnectedAccount: !!account?.id, // Check if account ID received is actually connected or exists. | |
accountId: account?.id, | |
hasCompletedProcess: account?.details_submitted, | |
isValid: account?.charges_enabled && account?.payouts_enabled, | |
displayName: | |
account?.settings?.dashboard?.display_name || | |
account?.display_name || | |
null, | |
country: account?.country, | |
currency: account?.default_currency, | |
}; | |
// boolean - Once the account is connected, should we let it unlink? | |
const shouldAllowUnlink = | |
accountAnalysis?.hasConnectedAccount && | |
(!accountAnalysis?.isValid || | |
!accountAnalysis?.hasCompletedProcess || | |
!accountAnalysis?.displayName); | |
return { oauth: result, account, accountAnalysis, shouldAllowUnlink }; | |
} catch (error) { | |
console.log(error); | |
} | |
}, | |
}) | |
.mutation("handlePayout", { | |
input: z.object({}), | |
async resolve({ ctx, input }) { | |
try { | |
} catch (error) {} | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment