117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import styles from "./styles.module.css";
|
|
|
|
import {
|
|
useStripe,
|
|
useElements,
|
|
ExpressCheckoutElement,
|
|
} from "@stripe/react-stripe-js";
|
|
import {
|
|
AvailablePaymentMethods,
|
|
StripeExpressCheckoutElementReadyEvent,
|
|
} from "@stripe/stripe-js";
|
|
|
|
interface IExpressCheckoutStripeProps {
|
|
clientSecret: string;
|
|
returnUrl?: string;
|
|
isHide?: boolean;
|
|
onAvailable?: (
|
|
isAvailable: boolean,
|
|
availableMethods: AvailablePaymentMethods | undefined
|
|
) => void;
|
|
onChangeLoading?: (isLoading: boolean) => void;
|
|
}
|
|
|
|
const checkFormAvailable = (
|
|
availablePaymentMethods: undefined | AvailablePaymentMethods
|
|
) => {
|
|
if (!availablePaymentMethods) return false;
|
|
let result = false;
|
|
for (const key in availablePaymentMethods) {
|
|
if (availablePaymentMethods[key as keyof AvailablePaymentMethods]) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
function ExpressCheckoutStripe({
|
|
clientSecret,
|
|
returnUrl = "https://${window.location.host}/payment/result/",
|
|
isHide = false,
|
|
onAvailable,
|
|
onChangeLoading,
|
|
}: IExpressCheckoutStripeProps) {
|
|
const stripe = useStripe();
|
|
const elements = useElements();
|
|
const [errorMessage, setErrorMessage] = useState<string>();
|
|
const [isAvailable, setIsAvailable] = useState(false);
|
|
const isHideForm = useMemo(
|
|
() => isHide || !isAvailable,
|
|
[isAvailable, isHide]
|
|
);
|
|
|
|
const onConfirm = async () =>
|
|
// event: StripeExpressCheckoutElementConfirmEvent
|
|
{
|
|
if (!stripe || !elements) {
|
|
// Stripe.js hasn't loaded yet.
|
|
// Make sure to disable form submission until Stripe.js has loaded.
|
|
return;
|
|
}
|
|
|
|
const { error: submitError } = await elements.submit();
|
|
if (submitError) {
|
|
setErrorMessage(submitError.message);
|
|
return;
|
|
}
|
|
|
|
// // Create the PaymentIntent and obtain clientSecret
|
|
// const res = await fetch("/create-intent", {
|
|
// method: "POST",
|
|
// });
|
|
// const { client_secret: clientSecret } = await res.json();
|
|
|
|
// Confirm the PaymentIntent using the details collected by the Express Checkout Element
|
|
const { error } = await stripe.confirmPayment({
|
|
// `elements` instance used to create the Express Checkout Element
|
|
elements,
|
|
// `clientSecret` from the created PaymentIntent
|
|
clientSecret,
|
|
confirmParams: {
|
|
return_url: returnUrl,
|
|
},
|
|
});
|
|
|
|
if (error) {
|
|
// This point is only reached if there's an immediate error when
|
|
// confirming the payment. Show the error to your customer (for example, payment details incomplete)
|
|
setErrorMessage(error.message);
|
|
} else {
|
|
// The payment UI automatically closes with a success animation.
|
|
// Your customer is redirected to your `return_url`.
|
|
}
|
|
};
|
|
|
|
const onReady = (event: StripeExpressCheckoutElementReadyEvent) => {
|
|
const _isAvailable = checkFormAvailable(event.availablePaymentMethods);
|
|
setIsAvailable(_isAvailable);
|
|
onAvailable && onAvailable(_isAvailable, event.availablePaymentMethods);
|
|
onChangeLoading && onChangeLoading(false);
|
|
};
|
|
|
|
return (
|
|
<div className={`${styles.container} ${isHideForm ? styles.hide : ""}`}>
|
|
<ExpressCheckoutElement
|
|
onReady={onReady}
|
|
onLoadError={() => onChangeLoading && onChangeLoading(false)}
|
|
onConfirm={onConfirm}
|
|
/>
|
|
{errorMessage && <p className={styles.error}>{errorMessage}</p>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default ExpressCheckoutStripe;
|