Fixes/stripe improvements (#5698)

* Enables require confirmation after payment its made

* Various Stripe improvements

* Use constant

* Merge branch 'main' into fixes/stripe-improvements

* Update getStripeAppKeys.ts

* submodule sync

* Submodule sync

* Revert

* Update apps/web/components/booking/BookingListItem.tsx

Co-authored-by: alannnc <alannnc@gmail.com>

Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2022-12-21 20:31:09 +00:00
1 parent 2494655369
commit 02425702d1
12 files changed
+77 -21

No files matched your search

+7 -1
View File
@@ -15,6 +15,7 @@ export default function AppCard({
switchChecked,
children,
setAppData,
returnTo,
}: {
app: RouterOutputs["viewer"]["apps"][number];
description?: React.ReactNode;
@@ -22,6 +23,7 @@ export default function AppCard({
switchOnClick?: (e: boolean) => void;
children?: React.ReactNode;
setAppData: SetAppDataGeneric<typeof eventTypeAppCardZod>;
returnTo?: string;
}) {
const [animationRef] = useAutoAnimate<HTMLDivElement>();
@@ -55,7 +57,11 @@ export default function AppCard({
/>
</div>
) : (
<OmniInstallAppButton className="ml-auto flex items-center" appId={app.slug} />
<OmniInstallAppButton
className="ml-auto flex items-center"
appId={app.slug}
returnTo={returnTo}
/>
)}
</div>
</div>
@@ -11,12 +11,21 @@ import { InstallAppButton } from "../components";
* Use this component to allow installing an app from anywhere on the app.
* Use of this component requires you to remove custom InstallAppButtonComponent so that it can manage the redirection itself
*/
export default function OmniInstallAppButton({ appId, className }: { appId: string; className: string }) {
export default function OmniInstallAppButton({
appId,
className,
returnTo,
}: {
appId: string;
className: string;
returnTo?: string;
}) {
const { t } = useLocale();
const { data: app } = useApp(appId);
const utils = trpc.useContext();
const mutation = useAddAppMutation(null, {
returnTo,
onSuccess: (data) => {
//TODO: viewer.appById might be replaced with viewer.apps so that a single query needs to be invalidated.
utils.viewer.appById.invalidate({ appId });
@@ -21,9 +21,11 @@ type CustomUseMutationOptions =
type AddAppMutationData = { setupPending: boolean } | void;
type UseAddAppMutationOptions = CustomUseMutationOptions & {
onSuccess: (data: AddAppMutationData) => void;
returnTo?: string;
};
function useAddAppMutation(_type: App["type"] | null, options?: UseAddAppMutationOptions) {
function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMutationOptions) {
const { returnTo, ...options } = allOptions || {};
const mutation = useMutation<
AddAppMutationData,
Error,
@@ -42,11 +44,12 @@ function useAddAppMutation(_type: App["type"] | null, options?: UseAddAppMutatio
}
const state: IntegrationOAuthCallbackState = {
returnTo:
returnTo ||
WEBAPP_URL +
getInstalledAppPath(
{ variant: variables && variables.variant, slug: variables && variables.slug },
location.search
),
getInstalledAppPath(
{ variant: variables && variables.variant, slug: variables && variables.slug },
location.search
),
};
const stateStr = encodeURIComponent(JSON.stringify(state));
const searchParams = `?state=${stateStr}`;
+2 -1
View File
@@ -20,7 +20,8 @@
"@calcom/trpc": "*",
"@calcom/ui": "*",
"@calcom/zoomvideo": "*",
"lodash": "^4.17.21"
"lodash": "^4.17.21",
"qs-stringify": "^1.2.1"
},
"devDependencies": {
"@calcom/types": "*"
+19 -10
View File
@@ -1,12 +1,16 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { stringify } from "querystring";
import stringify from "qs-stringify";
import Stripe from "stripe";
import { z } from "zod";
import { BASE_URL } from "@calcom/lib/constants";
import { WEBAPP_URL } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";
const client_id = process.env.STRIPE_CLIENT_ID;
import { getStripeAppKeys } from "../lib/getStripeAppKeys";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { client_id } = await getStripeAppKeys();
if (req.method === "GET") {
// Get user
const user = await prisma.user.findUnique({
@@ -19,18 +23,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
});
const redirect_uri = encodeURI(BASE_URL + "/api/integrations/stripepayment/callback");
const stripeConnectParams = {
const redirect_uri = encodeURI(WEBAPP_URL + "/api/integrations/stripepayment/callback");
const stripeConnectParams: Stripe.OAuthAuthorizeUrlParams = {
client_id,
scope: "read_write",
response_type: "code",
"stripe_user[email]": user?.email,
"stripe_user[first_name]": user?.name,
/** We need this so E2E don't fail for international users */
"stripe_user[country]": process.env.NEXT_PUBLIC_IS_E2E ? "US" : undefined,
stripe_user: {
email: user?.email,
first_name: user?.name || undefined,
/** We need this so E2E don't fail for international users */
country: process.env.NEXT_PUBLIC_IS_E2E ? "US" : undefined,
},
redirect_uri,
state: typeof req.query.state === "string" ? req.query.state : undefined,
};
const query = stringify(stripeConnectParams);
/** stringify is being dumb here */
const params = z.record(z.any()).parse(stripeConnectParams);
const query = stringify(params);
/**
* Choose Express or Standard Stripe accounts
* @url https://stripe.com/docs/connect/accounts
@@ -7,6 +7,16 @@ import prisma from "@calcom/prisma";
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
import stripe, { StripeData } from "../lib/server";
function getReturnToValueFromQueryState(req: NextApiRequest) {
let returnTo = "";
try {
returnTo = JSON.parse(`${req.query.state}`).returnTo;
} catch (error) {
console.info("No 'returnTo' in req.query.state");
}
return returnTo;
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { code, error, error_description } = req.query;
@@ -40,5 +50,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
});
res.redirect(getInstalledAppPath({ variant: "payment", slug: "stripe" }));
const returnTo = getReturnToValueFromQueryState(req);
res.redirect(returnTo || getInstalledAppPath({ variant: "payment", slug: "stripe" }));
}
@@ -3,6 +3,7 @@ import Stripe from "stripe";
import { getPremiumMonthlyPlanPriceId } from "@calcom/app-store/stripepayment/lib/utils";
import { checkPremiumUsername } from "@calcom/features/ee/common/lib/checkPremiumUsername";
import { WEBAPP_URL } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
@@ -36,7 +37,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return;
}
const return_url = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/integrations/stripepayment/paymentCallback?checkoutSessionId={CHECKOUT_SESSION_ID}&callbackUrl=${callbackUrl}`;
const return_url = `${WEBAPP_URL}/api/integrations/stripepayment/paymentCallback?checkoutSessionId={CHECKOUT_SESSION_ID}&callbackUrl=${callbackUrl}`;
const createSessionParams: Stripe.Checkout.SessionCreateParams = {
mode: "subscription",
payment_method_types: ["card"],
@@ -1,15 +1,18 @@
import { useRouter } from "next/router";
import { useState } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
import AppCard from "@calcom/app-store/_components/AppCard";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Alert, TextField } from "@calcom/ui";
import { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const { asPath } = useRouter();
const [getAppData, setAppData] = useAppContextWithSchema<typeof appDataSchema>();
const price = getAppData("price");
const currency = getAppData("currency");
@@ -28,6 +31,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
.trim();
return (
<AppCard
returnTo={WEBAPP_URL + asPath}
setAppData={setAppData}
app={app}
switchChecked={requirePayment}
@@ -1,5 +1,7 @@
import { stringify } from "querystring";
import { WEBSITE_URL } from "@calcom/lib/constants";
export type Maybe<T> = T | undefined | null;
export function createPaymentLink(opts: {
@@ -11,7 +13,7 @@ export function createPaymentLink(opts: {
}): string {
const { paymentUid, name, email, date, absolute = true } = opts;
let link = "";
if (absolute) link = process.env.NEXT_PUBLIC_WEBSITE_URL!;
if (absolute) link = WEBSITE_URL;
const query = stringify({ date, name, email });
return link + `/payment/${paymentUid}?${query}`;
}
@@ -0,0 +1,4 @@
import getParsedAppKeysFromSlug from "../../_utils/getParsedAppKeysFromSlug";
import { appKeysSchema } from "../zod";
export const getStripeAppKeys = () => getParsedAppKeysFromSlug("stripe", appKeysSchema);
@@ -34,6 +34,7 @@ export const stripeDataSchema = stripeOAuthTokenSchema.extend({
export type StripeData = z.infer<typeof stripeDataSchema>;
/** Figure out a way to get this from the DB without too much wreckage. */
const stripePrivateKey = process.env.STRIPE_PRIVATE_KEY!;
const stripe = new Stripe(stripePrivateKey, {
apiVersion: "2020-08-27",
+5
View File
@@ -21971,6 +21971,11 @@ qrcode@^1.5.1:
pngjs "^5.0.0"
yargs "^15.3.1"
qs-stringify@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/qs-stringify/-/qs-stringify-1.2.1.tgz#9b39ef6b816bd83309628fc9dad435fc0eccc28b"
integrity sha512-2N5xGLGZUxpgAYq1fD1LmBSCbxQVsXYt5JU0nU3FuPWO8PlCnKNFQwXkZgyB6mrTdg7IbexX4wxIR403dJw9pw==
qs@6.10.3, qs@^6.10.2, qs@^6.10.3, qs@^6.7.0, qs@^6.9.4:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"