Skip to content

Commit

Permalink
Fix checkout Stripe bug (#1018)
Browse files Browse the repository at this point in the history
  • Loading branch information
typeofweb authored Nov 13, 2023
1 parent 1d13c57 commit 01c61de
Show file tree
Hide file tree
Showing 15 changed files with 79 additions and 69 deletions.
6 changes: 3 additions & 3 deletions src/checkout/assets/icons/alert.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export function AlertIcon() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="9" stroke="#B65757" stroke-width="2" />
<path d="M12 12L12 7" stroke="#B65757" stroke-width="2" stroke-linecap="round" />
<path d="M12 16L12 15" stroke="#B65757" stroke-width="2" stroke-linecap="round" />
<circle cx="12" cy="12" r="9" stroke="#B65757" strokeWidth="2" />
<path d="M12 12L12 7" stroke="#B65757" strokeWidth="2" stroke-linecap="round" />
<path d="M12 16L12 15" stroke="#B65757" strokeWidth="2" stroke-linecap="round" />
</svg>
);
}
6 changes: 3 additions & 3 deletions src/checkout/assets/icons/error.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export function ErrorIcon() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="9" stroke="#B65757" stroke-width="2" />
<path d="M15.5355 8.46446L8.46447 15.5355" stroke="#B65757" stroke-width="2" stroke-linecap="round" />
<path d="M15.5355 15.5355L8.46447 8.46447" stroke="#B65757" stroke-width="2" stroke-linecap="round" />
<circle cx="12" cy="12" r="9" stroke="#B65757" strokeWidth="2" />
<path d="M15.5355 8.46446L8.46447 15.5355" stroke="#B65757" strokeWidth="2" stroke-linecap="round" />
<path d="M15.5355 15.5355L8.46447 8.46447" stroke="#B65757" strokeWidth="2" stroke-linecap="round" />
</svg>
);
}
2 changes: 1 addition & 1 deletion src/checkout/assets/icons/eye.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function EyeIcon() {
d="M2 12L1.34438 11.6358L1.14203 12L1.34438 12.3642L2 12ZM22 12L22.6556 12.3642L22.858 12L22.6556 11.6358L22 12ZM2 12C2.65562 12.3642 2.65548 12.3645 2.65535 12.3647C2.65533 12.3648 2.65521 12.365 2.65516 12.365C2.65506 12.3652 2.65501 12.3653 2.65501 12.3653C2.65501 12.3653 2.6552 12.365 2.6556 12.3643C2.65639 12.3629 2.65798 12.3601 2.66037 12.3559C2.66515 12.3476 2.67312 12.3338 2.68428 12.315C2.7066 12.2773 2.74165 12.2194 2.78941 12.1442C2.88498 11.9937 3.03112 11.7743 3.22767 11.5089C3.62151 10.9772 4.21352 10.2669 5.00172 9.55747C6.57889 8.13802 8.90793 6.75 12 6.75V5.25C8.4254 5.25 5.75444 6.86198 3.99828 8.44253C3.11982 9.23314 2.46183 10.0228 2.02233 10.6161C1.80222 10.9132 1.63585 11.1626 1.52309 11.3402C1.46668 11.429 1.42361 11.5 1.39384 11.5503C1.37896 11.5754 1.3674 11.5953 1.35916 11.6097C1.35504 11.6169 1.35175 11.6227 1.34928 11.627C1.34805 11.6292 1.34703 11.631 1.34621 11.6325C1.3458 11.6332 1.34545 11.6339 1.34514 11.6344C1.34499 11.6347 1.3448 11.635 1.34472 11.6352C1.34455 11.6355 1.34438 11.6358 2 12ZM12 6.75C15.0921 6.75 17.4211 8.13802 18.9983 9.55747C19.7865 10.2669 20.3785 10.9772 20.7723 11.5089C20.9689 11.7743 21.115 11.9937 21.2106 12.1442C21.2583 12.2194 21.2934 12.2773 21.3157 12.315C21.3269 12.3338 21.3348 12.3476 21.3396 12.3559C21.342 12.3601 21.3436 12.3629 21.3444 12.3643C21.3448 12.365 21.345 12.3653 21.345 12.3653C21.345 12.3653 21.3449 12.3652 21.3448 12.365C21.3448 12.365 21.3447 12.3648 21.3446 12.3647C21.3445 12.3645 21.3444 12.3642 22 12C22.6556 11.6358 22.6555 11.6355 22.6553 11.6352C22.6552 11.635 22.655 11.6347 22.6549 11.6344C22.6546 11.6339 22.6542 11.6332 22.6538 11.6325C22.653 11.631 22.6519 11.6292 22.6507 11.627C22.6483 11.6227 22.645 11.6169 22.6408 11.6097C22.6326 11.5953 22.621 11.5754 22.6062 11.5503C22.5764 11.5 22.5333 11.429 22.4769 11.3402C22.3641 11.1626 22.1978 10.9132 21.9777 10.6161C21.5382 10.0228 20.8802 9.23314 20.0017 8.44253C18.2456 6.86198 15.5746 5.25 12 5.25V6.75ZM22 12C21.3444 11.6358 21.3445 11.6355 21.3446 11.6353C21.3447 11.6352 21.3448 11.635 21.3448 11.635C21.3449 11.6348 21.345 11.6347 21.345 11.6347C21.345 11.6347 21.3448 11.635 21.3444 11.6357C21.3436 11.6371 21.342 11.6399 21.3396 11.6441C21.3348 11.6524 21.3269 11.6662 21.3157 11.685C21.2934 11.7227 21.2583 11.7806 21.2106 11.8558C21.115 12.0063 20.9689 12.2257 20.7723 12.4911C20.3785 13.0228 19.7865 13.7331 18.9983 14.4425C17.4211 15.862 15.0921 17.25 12 17.25V18.75C15.5746 18.75 18.2456 17.138 20.0017 15.5575C20.8802 14.7669 21.5382 13.9772 21.9777 13.3839C22.1978 13.0868 22.3641 12.8374 22.4769 12.6598C22.5333 12.571 22.5764 12.5 22.6062 12.4497C22.621 12.4246 22.6326 12.4047 22.6408 12.3903C22.645 12.3831 22.6483 12.3773 22.6507 12.373C22.6519 12.3708 22.653 12.369 22.6538 12.3675C22.6542 12.3668 22.6546 12.3661 22.6549 12.3656C22.655 12.3653 22.6552 12.365 22.6553 12.3648C22.6555 12.3645 22.6556 12.3642 22 12ZM12 17.25C8.90793 17.25 6.57889 15.862 5.00172 14.4425C4.21352 13.7331 3.62151 13.0228 3.22767 12.4911C3.03112 12.2257 2.88498 12.0063 2.78941 11.8558C2.74165 11.7806 2.7066 11.7227 2.68428 11.685C2.67312 11.6662 2.66515 11.6524 2.66037 11.6441C2.65798 11.6399 2.65639 11.6371 2.6556 11.6357C2.6552 11.635 2.65501 11.6347 2.65501 11.6347C2.65501 11.6347 2.65506 11.6348 2.65516 11.635C2.65521 11.635 2.65533 11.6352 2.65535 11.6353C2.65548 11.6355 2.65562 11.6358 2 12C1.34438 12.3642 1.34455 12.3645 1.34472 12.3648C1.3448 12.365 1.34499 12.3653 1.34514 12.3656C1.34545 12.3661 1.3458 12.3668 1.34621 12.3675C1.34703 12.369 1.34805 12.3708 1.34928 12.373C1.35175 12.3773 1.35504 12.3831 1.35916 12.3903C1.3674 12.4047 1.37896 12.4246 1.39384 12.4497C1.42361 12.5 1.46668 12.571 1.52309 12.6598C1.63585 12.8374 1.80222 13.0868 2.02233 13.3839C2.46183 13.9772 3.11982 14.7669 3.99828 15.5575C5.75444 17.138 8.4254 18.75 12 18.75V17.25Z"
fill="#394052"
/>
<circle cx="12" cy="12" r="3.25" stroke="#394052" stroke-width="1.5" />
<circle cx="12" cy="12" r="3.25" stroke="#394052" strokeWidth="1.5" />
</svg>
);
}
6 changes: 3 additions & 3 deletions src/checkout/assets/icons/eyeHidden.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ export function EyeHiddenIcon() {
<path
d="M12 6C5.33333 6 2 12 2 12C2 12 5.33333 18 12 18C18.6667 18 22 12 22 12C22 12 18.6667 6 12 6Z"
stroke="#394052"
stroke-width="1.5"
strokeWidth="1.5"
/>
<circle cx="12" cy="12" r="3.25" stroke="#394052" stroke-width="1.5" />
<path d="M20 4L4 20" stroke="#394052" stroke-width="1.5" />
<circle cx="12" cy="12" r="3.25" stroke="#394052" strokeWidth="1.5" />
<path d="M20 4L4 20" stroke="#394052" strokeWidth="1.5" />
</svg>
);
}
4 changes: 2 additions & 2 deletions src/checkout/assets/icons/language.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ export function LanguageIcon() {
d="M4.16216 17.1351C6.39798 15.0293 8.09695 13.3202 9.38788 11.856M13.3514 4.16216H16.5946M13.3514 4.16216H9.56757M13.3514 4.16216C13.0861 6.61538 12.2166 8.64768 9.38788 11.856M2 4.16216H9.56757M9.56757 4.16216V2M9.38788 11.856C10.4884 12.9649 11.1238 13.5119 12.2703 14.4324M9.38788 11.856C7.84065 10.351 6.32432 7.94595 6.32432 7.40541"
stroke="#394052"
stroke-opacity="0.8"
stroke-width="1.5"
strokeWidth="1.5"
stroke-linejoin="bevel"
/>
<path
d="M13.3513 20.9189L14.7928 17.1351M22 20.9189L20.5585 17.1351M14.7928 17.1351L17.6756 9.56755L20.5585 17.1351M14.7928 17.1351H20.5585"
stroke="#394052"
stroke-opacity="0.8"
stroke-width="1.5"
strokeWidth="1.5"
stroke-linejoin="bevel"
/>
</g>
Expand Down
4 changes: 2 additions & 2 deletions src/checkout/assets/icons/success.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export function SuccessIcon() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="9" stroke="#2C9B2A" stroke-width="2" />
<path d="M8 12L10.5 14.5L16 9" stroke="#2C9B2A" stroke-width="2" />
<circle cx="12" cy="12" r="9" stroke="#2C9B2A" strokeWidth="2" />
<path d="M8 12L10.5 14.5L16 9" stroke="#2C9B2A" strokeWidth="2" />
</svg>
);
}
7 changes: 6 additions & 1 deletion src/checkout/hooks/useCheckoutComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ export const useCheckoutComplete = () => {
const order = data.order;

if (order) {
const newUrl = replaceUrl({ query: { checkout: undefined, order: order.id } });
const newUrl = replaceUrl({
query: {
order: order.id,
},
replaceWholeQuery: true,
});
window.location.href = newUrl;
}
},
Expand Down
13 changes: 8 additions & 5 deletions src/checkout/lib/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const queryParamsMap = {
redirectResult: "redirectResult",
resultCode: "resultCode",
type: "type",
// stripe
payment_intent: "paymentIntent",
payment_intent_client_secret: "paymentIntentClientSecret",
} as const;

type UnmappedQueryParam = keyof typeof queryParamsMap;
Expand Down Expand Up @@ -71,29 +74,29 @@ export const clearQueryParams = (...keys: QueryParam[]) => {
};

export const getUrl = ({
url = window.location.toString(),
query,
replaceWholeQuery = false,
}: {
url?: string;
query?: Record<string, any>;
replaceWholeQuery?: boolean;
}) => {
const baseUrl = replaceWholeQuery
? window.location.toString().replace(window.location.search, "")
: window.location.toString();
const newQuery = replaceWholeQuery ? query : { ...getRawQueryParams(), ...query };
const newUrl = queryString.stringifyUrl({ url, query: newQuery });
const newUrl = queryString.stringifyUrl({ url: baseUrl, query: newQuery });
return { newUrl, newQuery };
};

export const replaceUrl = ({
url = window.location.toString(),
query,
replaceWholeQuery = false,
}: {
url?: string;
query?: Record<string, any>;
replaceWholeQuery?: boolean;
}) => {
const { newUrl, newQuery } = getUrl({ url, query, replaceWholeQuery });
const { newUrl, newQuery } = getUrl({ query, replaceWholeQuery });

window.history.pushState(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,12 @@ export function createAdyenCheckoutInstance(
buttonType: "plain",
buttonColor: "black",
onPaymentMethodSelected: (resolve: ApplePayCallback, reject: ApplePayCallback, event) => {
console.log({ "event.paymentMethod": event.paymentMethod, event });
resolve(event.paymentMethod);
},
onShippingContactSelected: (resolve: ApplePayCallback, reject: ApplePayCallback, event) => {
console.log({ "event.shippingContact": event.shippingContact, event });
resolve(event.shippingContact);
},
onShippingMethodSelected: (resolve: ApplePayCallback, reject: ApplePayCallback, event) => {
console.log({ "event.shippingMethod": event.shippingMethod, event });
resolve(event.shippingMethod);
},
},
Expand Down
38 changes: 9 additions & 29 deletions src/checkout/sections/PaymentSection/PaymentProcessingScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,35 @@
import { BarLoader } from "react-spinners";
import React, { useEffect, useState } from "react";
import React, { type ReactNode, useState, useCallback, useMemo } from "react";
import { Title } from "@/checkout/components";
import { SaleorLogo } from "@/checkout/assets/images/SaleorLogo";
import { type Children } from "@/checkout/lib/globalTypes";
import { createSafeContext } from "@/checkout/providers/createSafeContext";
import { getQueryParams } from "@/checkout/lib/utils/url";
import { PAGE_ID } from "@/checkout/views/Checkout/consts";

interface PaymentProcessingContextConsumerProps {
setIsProcessingPayment: (processing: boolean) => void;
}

const [usePaymentProcessingScreen, Provider] = createSafeContext<PaymentProcessingContextConsumerProps>();

interface PaymentProcessingScreenProps extends Children {}

export const PaymentProcessingScreen: React.FC<PaymentProcessingScreenProps> = ({ children }) => {
const handleSetStyles = (processing: boolean) => {
const el = document.getElementById(PAGE_ID);

if (el) {
el.style.maxHeight = processing ? "100vh" : "auto";
el.style.overflow = processing ? "hidden" : "auto";
}
};

export const PaymentProcessingScreen = ({ children }: { children: ReactNode }) => {
const getInitialProcessing = () => {
const { transaction, processingPayment } = getQueryParams();
const { processingPayment } = getQueryParams();

return !!(transaction && processingPayment);
return !!processingPayment;
};

const [isProcessingPayment, setIsProcessingPayment] = useState(getInitialProcessing());

const handleSetProcessing = (processing: boolean) => {
handleSetStyles(processing);
const handleSetProcessing = useCallback((processing: boolean) => {
setIsProcessingPayment(processing);
};

useEffect(() => {
handleSetStyles(isProcessingPayment);
}, [isProcessingPayment]);
}, []);

return (
<Provider value={{ setIsProcessingPayment: handleSetProcessing }}>
<Provider value={useMemo(() => ({ setIsProcessingPayment: handleSetProcessing }), [handleSetProcessing])}>
{isProcessingPayment && (
<div className="z-1000 bg-background-primary absolute left-0 top-0 m-auto flex h-screen w-screen flex-col items-center">
<SaleorLogo />
<div className="fixed inset-0 z-50 flex flex-col items-center bg-gray-100">
<div className="flex flex-grow flex-col justify-center pb-40">
<Title>Almost done…</Title>
<BarLoader />
<BarLoader width="100%" />
</div>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type FormEventHandler, useEffect, useState } from "react";
import { PaymentElement, useStripe, useElements } from "@stripe/react-stripe-js";
import { type StripePaymentElementOptions } from "@stripe/stripe-js";
import { getUrlForTransactionInitialize } from "../utils";
import { usePaymentProcessingScreen } from "../PaymentProcessingScreen";
import {
useCheckoutValidationActions,
useCheckoutValidationState,
Expand All @@ -18,6 +19,8 @@ import { useEvent } from "@/checkout/hooks/useEvent";
import { useUser } from "@/checkout/hooks/useUser";
import { useAlerts } from "@/checkout/hooks/useAlerts";
import { useCheckout } from "@/checkout/hooks/useCheckout";
import { useCheckoutComplete } from "@/checkout/hooks/useCheckoutComplete";
import { getQueryParams } from "@/checkout/lib/utils/url";

const paymentElementOptions: StripePaymentElementOptions = {
layout: "tabs",
Expand All @@ -42,6 +45,9 @@ export function CheckoutForm() {
const { validateAllForms } = useCheckoutValidationActions();
const { validationState } = useCheckoutValidationState();

const { setIsProcessingPayment } = usePaymentProcessingScreen();
const { onCheckoutComplete, completingCheckout } = useCheckoutComplete();

// handler for when user presses submit
const onSubmitInitialize: FormEventHandler<HTMLFormElement> = useEvent(async (e) => {
e.preventDefault();
Expand All @@ -52,6 +58,19 @@ export function CheckoutForm() {
setSubmitInProgress(true);
});

// handle when page is opened from previously redirected payment
useEffect(() => {
const { paymentIntent, paymentIntentClientSecret, processingPayment } = getQueryParams();

if (!paymentIntent || !paymentIntentClientSecret || !processingPayment) {
return;
}

if (!completingCheckout) {
void onCheckoutComplete();
}
}, [completingCheckout, onCheckoutComplete]);

// when submission is initialized, awaits for all the other requests to finish,
// forms to validate, then either does transaction initialize or process
useEffect(() => {
Expand Down Expand Up @@ -101,23 +120,31 @@ export function CheckoutForm() {
},
},
})
.then(({ error }) => {
.then(async ({ error }) => {
console.error(error);
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
showCustomErrors([{ message: error.message ?? "Something went wrong" }]);
} else {
showCustomErrors([{ message: "An unexpected error occurred." }]);
setIsLoading(false);

if (error) {
setIsProcessingPayment(false);
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
showCustomErrors([{ message: error.message ?? "Something went wrong" }]);
} else {
showCustomErrors([{ message: "An unexpected error occurred." }]);
}
return;
}

setIsLoading(false);
return onCheckoutComplete();
})
.catch((err) => {
console.error(err);
setIsLoading(false);
setIsProcessingPayment(false);
});

// @todo
Expand All @@ -144,6 +171,8 @@ export function CheckoutForm() {
checkoutUpdateState.submitInProgress,
elements,
finishedApiChangesWithNoError,
onCheckoutComplete,
setIsProcessingPayment,
setSubmitInProgress,
showCustomErrors,
stripe,
Expand Down
2 changes: 1 addition & 1 deletion src/checkout/state/updateStateStore/updateStateStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const useCheckoutUpdateStateStore = createWithEqualityFn<CheckoutUpdateStateStor
}),
shallow,
);
useCheckoutUpdateStateStore.subscribe(console.log);
// useCheckoutUpdateStateStore.subscribe(console.log);

export const useCheckoutUpdateState = (): CheckoutUpdateState => {
const { updateState, loadingCheckout, submitInProgress, changingBillingCountry } =
Expand Down
3 changes: 1 addition & 2 deletions src/checkout/views/Checkout/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Summary, SummarySkeleton } from "@/checkout/sections/Summary";
import { CheckoutForm, CheckoutFormSkeleton } from "@/checkout/sections/CheckoutForm";
import { useCheckout } from "@/checkout/hooks/useCheckout";
import { CheckoutSkeleton } from "@/checkout/views/Checkout/CheckoutSkeleton";
import { PAGE_ID } from "@/checkout/views/Checkout/consts";

export const Checkout = () => {
const { checkout, fetching: fetchingCheckout } = useCheckout();
Expand All @@ -26,7 +25,7 @@ export const Checkout = () => {
<CheckoutSkeleton />
) : (
<ErrorBoundary FallbackComponent={PageNotFound}>
<div className="page" id={PAGE_ID}>
<div className="page">
<PageHeader />
{isEmptyCart ? (
<EmptyCartPage />
Expand Down
1 change: 0 additions & 1 deletion src/checkout/views/Checkout/consts.ts

This file was deleted.

2 changes: 0 additions & 2 deletions src/ui/components/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,9 @@ export function AuthProvider({ children }: { children: ReactNode }) {
useAuthChange({
saleorApiUrl,
onSignedOut: () => {
console.log("onSignedOut");
setUrqlClient(makeUrqlClient());
},
onSignedIn: () => {
console.log("onSignedIn");
setUrqlClient(makeUrqlClient());
},
});
Expand Down

0 comments on commit 01c61de

Please sign in to comment.