|
@@ -1,32 +1,164 @@
|
|
|
"use client";
|
|
"use client";
|
|
|
|
|
|
|
|
-import {useState, useEffect} from "react";
|
|
|
|
|
-
|
|
|
|
|
|
|
+import {useState, useRef, useEffect } from "react";
|
|
|
|
|
+import { Payment } from "@airwallex/components-sdk";
|
|
|
import { LoadingSpinner } from "@components/common/LoadingSpinner";
|
|
import { LoadingSpinner } from "@components/common/LoadingSpinner";
|
|
|
-// import { useForm, SubmitHandler } from "react-hook-form"
|
|
|
|
|
-import { useForm, FormProvider } from "react-hook-form"
|
|
|
|
|
-
|
|
|
|
|
|
|
+import CheckoutShippingMethodLoading from "./CheckoutShippingMethodLoading";
|
|
|
|
|
+import { useForm, FormProvider } from "react-hook-form";
|
|
|
import {
|
|
import {
|
|
|
- Country,
|
|
|
|
|
ShipAddressFormData,
|
|
ShipAddressFormData,
|
|
|
- BillAddressFormData,
|
|
|
|
|
FullAddressFormData,
|
|
FullAddressFormData,
|
|
|
- CreateCheckoutAddressVariables
|
|
|
|
|
|
|
+ CreateCheckoutAddressVariables,
|
|
|
|
|
+ ExtraMapPlaceOrderResult,
|
|
|
|
|
+ PlaceOrderResult,
|
|
|
|
|
+ PlaceOrderFunction
|
|
|
} from "@/types/checkout/type";
|
|
} from "@/types/checkout/type";
|
|
|
import AddressResultDisplay from "./AddressResultDisplay";
|
|
import AddressResultDisplay from "./AddressResultDisplay";
|
|
|
-import AddressResultDisplayLoading from "./AddressResultDisplayLoading";
|
|
|
|
|
-import CheckoutAddressModal from "./CheckoutAddressModal";
|
|
|
|
|
import ShippingAddressCheckout from "./ShippingAddressCheckout";
|
|
import ShippingAddressCheckout from "./ShippingAddressCheckout";
|
|
|
import BillingAddressCheckout from "./BillingAddressCheckout";
|
|
import BillingAddressCheckout from "./BillingAddressCheckout";
|
|
|
-// import CheckoutShippingMethod from "./CheckoutShippingMethod";
|
|
|
|
|
|
|
+import {ShippingMethodCheckout,RefShippingMethodsHandle} from "./ShippingMethodCheckout";
|
|
|
import { useCustomToast } from "@/utils/hooks/useToast";
|
|
import { useCustomToast } from "@/utils/hooks/useToast";
|
|
|
import {useCheckoutAddress} from "@/utils/hooks/useCheckoutAddress"
|
|
import {useCheckoutAddress} from "@/utils/hooks/useCheckoutAddress"
|
|
|
|
|
+import {useCheckoutPaymentMethod} from "@/utils/hooks/useCheckoutPaymentMethod";
|
|
|
|
|
+import {useCheckoutShippingMethod} from "@/utils/hooks/useCheckoutShippingMethod";
|
|
|
|
|
+import { useAppDispatch } from "@/store/hooks";
|
|
|
|
|
+import { updateCart } from "@/store/slices/cart-slice";
|
|
|
|
|
+import {PaymentMethodCheckout,RefPaymentMethodsHandle} from "./PaymentMethodCheckout";
|
|
|
|
|
+import LoadingPaymentMethod from "./LoadingPaymentMethod";
|
|
|
|
|
+import LoadingCheckoutPlaceOrder from "./LoadingCheckoutPlaceOrder";
|
|
|
|
|
+import CheckoutPlaceOrder from "./CheckoutPlaceOrder";
|
|
|
|
|
+import CommonModal from "@/components/theme/ui/CommonModal";
|
|
|
|
|
+import {useSaveCheckoutCart} from "@/utils/hooks/useSaveCheckoutCart";
|
|
|
|
|
+import { formatCartDetail, shippingAddressToCartAddress, billingAddressToCartAddress } from "@/utils/cartDetailTools";
|
|
|
|
|
+import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
|
|
|
|
|
+import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
|
|
|
|
|
+import { CartDetail,CartAddress } from "@/types/cart/type";
|
|
|
|
|
+import {normalizePhoneForForm} from "@/utils/phoneNumberTools";
|
|
|
|
|
+import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
|
|
|
|
|
+import { clearCart } from "@/store/slices/cart-slice";
|
|
|
|
|
+import {loadAfterpayScript} from "@/lib/Afterpay/afterpay";
|
|
|
|
|
+import PaypalApplepayButton from "./PaymentButton/PaypalApplepayButton";
|
|
|
/***
|
|
/***
|
|
|
* 不用useForShipping字段了
|
|
* 不用useForShipping字段了
|
|
|
* 以shipping address 为准,根据shippingaddress 设置billing address
|
|
* 以shipping address 为准,根据shippingaddress 设置billing address
|
|
|
* 保存完地址之后要重新获取运输方式和支付方式
|
|
* 保存完地址之后要重新获取运输方式和支付方式
|
|
|
|
|
+ * 变更运输方式后重新获取支付方式
|
|
|
|
|
+ * @todo 购物车为空 跳转到空购物车页面
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 地址表单默认值
|
|
|
*/
|
|
*/
|
|
|
|
|
+function getBillingAddressFormData(billingAddress:CartAddress) {
|
|
|
|
|
+ const billingCountry = billingAddress.country || 'US';
|
|
|
|
|
+ const billingPhoneNumber = normalizePhoneForForm(billingAddress.phone, billingCountry) || "";
|
|
|
|
|
+ const res = {
|
|
|
|
|
+ "billingAddressId": billingAddress.id,
|
|
|
|
|
+ "billingEmail": billingAddress.email,
|
|
|
|
|
+ "billingFirstName": billingAddress.firstName,
|
|
|
|
|
+ "billingLastName": billingAddress.lastName,
|
|
|
|
|
+ "billingCompanyName": "",
|
|
|
|
|
+ "billingAddress": billingAddress.address,
|
|
|
|
|
+ "billingCountry": billingCountry,
|
|
|
|
|
+ "billingState": billingAddress.state,
|
|
|
|
|
+ "billingCity": billingAddress.city,
|
|
|
|
|
+ "billingPostcode": billingAddress.postcode,
|
|
|
|
|
+ "billingPhoneNumber": billingPhoneNumber,
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
|
|
+ return res;
|
|
|
|
|
+}
|
|
|
|
|
+function getShippingAddressFormData(shippingAddress:CartAddress) {
|
|
|
|
|
+ const shippingCountry = shippingAddress.country || 'US';
|
|
|
|
|
+ const shippingPhoneNumber = normalizePhoneForForm(shippingAddress.phone, shippingCountry) || "";
|
|
|
|
|
+ const res = {
|
|
|
|
|
+ "shippingAddressId": shippingAddress.id,
|
|
|
|
|
+ "shippingEmail": shippingAddress.email,
|
|
|
|
|
+ "shippingFirstName": shippingAddress.firstName,
|
|
|
|
|
+ "shippingLastName": shippingAddress.lastName,
|
|
|
|
|
+ "shippingCompanyName": "",
|
|
|
|
|
+ "shippingAddress": shippingAddress.address,
|
|
|
|
|
+ "shippingCountry": shippingCountry,
|
|
|
|
|
+ "shippingState": shippingAddress.state,
|
|
|
|
|
+ "shippingCity": shippingAddress.city,
|
|
|
|
|
+ "shippingPostcode": shippingAddress.postcode,
|
|
|
|
|
+ "shippingPhoneNumber": shippingPhoneNumber,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return res;
|
|
|
|
|
+}
|
|
|
|
|
+function getAddressFormDataFromCart(cartDetail:CartDetail, loginEmail?: string) {
|
|
|
|
|
+ const shippingAddress = cartDetail.shippingAddress;
|
|
|
|
|
+ const billingAddress = cartDetail.billingAddress;
|
|
|
|
|
+ const defaultValues = {
|
|
|
|
|
+ "shippingAddressId": "",
|
|
|
|
|
+ "shippingEmail": loginEmail ?? '',
|
|
|
|
|
+ "shippingFirstName": "",
|
|
|
|
|
+ "shippingLastName": "",
|
|
|
|
|
+ "shippingCompanyName": "",
|
|
|
|
|
+ "shippingAddress": "",
|
|
|
|
|
+ "shippingCountry": "US",
|
|
|
|
|
+ "shippingState": "",
|
|
|
|
|
+ "shippingCity": "",
|
|
|
|
|
+ "shippingPostcode": "",
|
|
|
|
|
+ "shippingPhoneNumber": "",
|
|
|
|
|
+ "billingAddressId": "",
|
|
|
|
|
+ "billingEmail": loginEmail ?? '',
|
|
|
|
|
+ "billingFirstName": "",
|
|
|
|
|
+ "billingLastName": "",
|
|
|
|
|
+ "billingCompanyName": "",
|
|
|
|
|
+ "billingAddress": "",
|
|
|
|
|
+ "billingCountry": "US",
|
|
|
|
|
+ "billingState": "",
|
|
|
|
|
+ "billingCity": "",
|
|
|
|
|
+ "billingPostcode": "",
|
|
|
|
|
+ "billingPhoneNumber": "",
|
|
|
|
|
+ "billingSameAsShipping": true
|
|
|
|
|
+ }
|
|
|
|
|
+ if(billingAddress) {
|
|
|
|
|
+ const bs = getBillingAddressFormData(billingAddress);
|
|
|
|
|
+ defaultValues.billingAddressId = bs.billingAddressId;
|
|
|
|
|
+ defaultValues.billingEmail = bs.billingEmail;
|
|
|
|
|
+ defaultValues.billingFirstName = bs.billingFirstName;
|
|
|
|
|
+ defaultValues.billingLastName = bs.billingLastName;
|
|
|
|
|
+ defaultValues.billingCompanyName = bs.billingCompanyName;
|
|
|
|
|
+ defaultValues.billingAddress = bs.billingAddress;
|
|
|
|
|
+ defaultValues.billingCountry = bs.billingCountry;
|
|
|
|
|
+ defaultValues.billingState = bs.billingState;
|
|
|
|
|
+ defaultValues.billingCity = bs.billingCity;
|
|
|
|
|
+ defaultValues.billingPostcode = bs.billingPostcode;
|
|
|
|
|
+ defaultValues.billingPhoneNumber = bs.billingPhoneNumber;
|
|
|
|
|
+ // defaultValues.billingSameAsShipping = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(shippingAddress) {
|
|
|
|
|
+ const ss = getShippingAddressFormData(shippingAddress);
|
|
|
|
|
+ defaultValues.shippingAddressId = ss.shippingAddressId;
|
|
|
|
|
+ defaultValues.shippingEmail = ss.shippingEmail;
|
|
|
|
|
+ defaultValues.shippingFirstName = ss.shippingFirstName;
|
|
|
|
|
+ defaultValues.shippingLastName = ss.shippingLastName;
|
|
|
|
|
+ defaultValues.shippingCompanyName = ss.shippingCompanyName;
|
|
|
|
|
+ defaultValues.shippingAddress = ss.shippingAddress;
|
|
|
|
|
+ defaultValues.shippingCountry = ss.shippingCountry;
|
|
|
|
|
+ defaultValues.shippingState = ss.shippingState;
|
|
|
|
|
+ defaultValues.shippingCity = ss.shippingCity;
|
|
|
|
|
+ defaultValues.shippingPostcode = ss.shippingPostcode;
|
|
|
|
|
+ defaultValues.shippingPhoneNumber = ss.shippingPhoneNumber;
|
|
|
|
|
+ }
|
|
|
|
|
+ if( defaultValues.billingEmail === defaultValues.shippingEmail &&
|
|
|
|
|
+ defaultValues.billingFirstName === defaultValues.shippingFirstName &&
|
|
|
|
|
+ defaultValues.billingLastName === defaultValues.shippingLastName &&
|
|
|
|
|
+ defaultValues.billingCompanyName === defaultValues.shippingCompanyName &&
|
|
|
|
|
+ defaultValues.billingAddress === defaultValues.shippingAddress &&
|
|
|
|
|
+ defaultValues.billingCountry === defaultValues.shippingCountry &&
|
|
|
|
|
+ defaultValues.billingState === defaultValues.shippingState &&
|
|
|
|
|
+ defaultValues.billingCity === defaultValues.shippingCity &&
|
|
|
|
|
+ defaultValues.billingPostcode === defaultValues.shippingPostcode &&
|
|
|
|
|
+ defaultValues.billingPhoneNumber === defaultValues.shippingPhoneNumber
|
|
|
|
|
+ ) {
|
|
|
|
|
+ defaultValues.billingSameAsShipping = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return defaultValues;
|
|
|
|
|
+}
|
|
|
/**
|
|
/**
|
|
|
* 生成createCheckoutAddress接口的参数
|
|
* 生成createCheckoutAddress接口的参数
|
|
|
*/
|
|
*/
|
|
@@ -81,82 +213,71 @@ function generateSaveCheckoutAddressParam(formData: FullAddressFormData):CreateC
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
-/**
|
|
|
|
|
- * 对比shipping address与billing address的数据是否完全相同
|
|
|
|
|
- */
|
|
|
|
|
-/*function addressIsSame(billAddress: BillAddressFormData | null, shipAddress: ShipAddressFormData | null) {
|
|
|
|
|
- if(billAddress === null && shipAddress === null) {
|
|
|
|
|
- return true;
|
|
|
|
|
- } else if(billAddress !== null && shipAddress !== null) {
|
|
|
|
|
- return (
|
|
|
|
|
- billAddress.billingEmail === shipAddress.shippingEmail &&
|
|
|
|
|
- billAddress.billingFirstName === shipAddress.shippingFirstName &&
|
|
|
|
|
- billAddress.billingLastName === shipAddress.shippingLastName &&
|
|
|
|
|
- billAddress.billingCompanyName === shipAddress.shippingCompanyName &&
|
|
|
|
|
- billAddress.billingAddress === shipAddress.shippingAddress &&
|
|
|
|
|
- billAddress.billingCity === shipAddress.shippingCity &&
|
|
|
|
|
- billAddress.billingState === shipAddress.shippingState &&
|
|
|
|
|
- billAddress.billingCountry === shipAddress.shippingCountry &&
|
|
|
|
|
- billAddress.billingPostcode === shipAddress.shippingPostcode &&
|
|
|
|
|
- billAddress.billingPhoneNumber === shipAddress.shippingPhoneNumber
|
|
|
|
|
- );
|
|
|
|
|
- } else {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-}*/
|
|
|
|
|
|
|
+
|
|
|
export default function CheckoutWrapper({
|
|
export default function CheckoutWrapper({
|
|
|
loginEmail,
|
|
loginEmail,
|
|
|
- countries
|
|
|
|
|
|
|
+ cartDetailData,
|
|
|
}: {
|
|
}: {
|
|
|
loginEmail: string;
|
|
loginEmail: string;
|
|
|
- countries: Country[]
|
|
|
|
|
|
|
+ cartDetailData: CartDetail;
|
|
|
}) {
|
|
}) {
|
|
|
|
|
+ const { createOrder } = usePlaceOrder();
|
|
|
|
|
+ const addressFormDefaultValues = getAddressFormDataFromCart(cartDetailData,loginEmail);
|
|
|
|
|
+ const dispatch = useAppDispatch();
|
|
|
|
|
+
|
|
|
|
|
+ const [cartData, setCartData] = useState<CartDetail>(cartDetailData);
|
|
|
|
|
+ const {saveCheckoutCart} = useSaveCheckoutCart();
|
|
|
|
|
+
|
|
|
|
|
+ const {
|
|
|
|
|
+ data: shippingMethodDatas,
|
|
|
|
|
+ loading: shippingMethodLoading,
|
|
|
|
|
+ error: shippingMethodError,
|
|
|
|
|
+ getShippingMethod,
|
|
|
|
|
+ } = useCheckoutShippingMethod();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ const selectedPaymentMethod = cartData.paymentMethod || 'paypal_smart_button';
|
|
|
|
|
+ const selectedShippingRate = cartData.selectedShippingRate;
|
|
|
|
|
+
|
|
|
|
|
+ const {
|
|
|
|
|
+ data: paymentMethodDatas,
|
|
|
|
|
+ loading: paymentMethodLoading,
|
|
|
|
|
+ error: paymentMethodError,
|
|
|
|
|
+ getPaymentMethod,
|
|
|
|
|
+ } = useCheckoutPaymentMethod();
|
|
|
|
|
+
|
|
|
const { showToast } = useCustomToast();
|
|
const { showToast } = useCustomToast();
|
|
|
const { getCheckoutAddress, saveCheckoutAddress } = useCheckoutAddress(loginEmail);
|
|
const { getCheckoutAddress, saveCheckoutAddress } = useCheckoutAddress(loginEmail);
|
|
|
|
|
|
|
|
|
|
+ const methodShiippingRef = useRef<RefShippingMethodsHandle>(null);
|
|
|
|
|
+ const methodPaymentRef = useRef<RefPaymentMethodsHandle>(null);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
|
|
|
// useForm() 只会在组件初始化时读取一次 defaultValues
|
|
// useForm() 只会在组件初始化时读取一次 defaultValues
|
|
|
const addressForm = useForm<FullAddressFormData>({
|
|
const addressForm = useForm<FullAddressFormData>({
|
|
|
- mode: "onBlur", // 或 "onChange"
|
|
|
|
|
- reValidateMode: "onChange",
|
|
|
|
|
|
|
+ mode: "onChange", // 或 "onChange"
|
|
|
|
|
+ reValidateMode: "onChange",
|
|
|
|
|
+ defaultValues: addressFormDefaultValues
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const [addressModalOpen, setAddressModalOpen] = useState(false);
|
|
const [addressModalOpen, setAddressModalOpen] = useState(false);
|
|
|
- const [loadingData, setLoadingData] = useState(true);
|
|
|
|
|
- const [serverShippingAddress, setServerShippingAddress] = useState<ShipAddressFormData | null>(null);
|
|
|
|
|
- const [serverBillingAddress, setServerBillingAddress] = useState<BillAddressFormData | null>(null);
|
|
|
|
|
|
|
|
|
|
- const [addressSaving, setAddressSaving] = useState(false);
|
|
|
|
|
|
|
|
|
|
- // 开发环境会执行两次 https://zh-hans.react.dev/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- getCheckoutAddress((shippingAddress,billingAddress)=> {
|
|
|
|
|
- addressForm.reset({
|
|
|
|
|
- ...shippingAddress,
|
|
|
|
|
- ...billingAddress,
|
|
|
|
|
- });
|
|
|
|
|
- setServerShippingAddress({
|
|
|
|
|
- ...shippingAddress
|
|
|
|
|
- });
|
|
|
|
|
- setServerBillingAddress({
|
|
|
|
|
- ...billingAddress
|
|
|
|
|
- });
|
|
|
|
|
- setLoadingData(false);
|
|
|
|
|
- }, ()=> {
|
|
|
|
|
- setLoadingData(false);
|
|
|
|
|
- });
|
|
|
|
|
- }, []);
|
|
|
|
|
|
|
+ const [addressSaving, setAddressSaving] = useState(false); // 保存地址时的loading状态
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
const openAddressModal = () => {
|
|
const openAddressModal = () => {
|
|
|
setAddressModalOpen(true);
|
|
setAddressModalOpen(true);
|
|
|
|
|
|
|
|
- if(serverShippingAddress && serverBillingAddress) {
|
|
|
|
|
|
|
+ if(cartData.billingAddress && cartData.shippingAddress) {
|
|
|
addressForm.reset({
|
|
addressForm.reset({
|
|
|
- ...serverShippingAddress,
|
|
|
|
|
- ...serverBillingAddress,
|
|
|
|
|
|
|
+ ...getAddressFormDataFromCart(cartData)
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
|
|
|
};
|
|
};
|
|
|
const closeAddressModal = (e:boolean) => {
|
|
const closeAddressModal = (e:boolean) => {
|
|
@@ -166,14 +287,20 @@ export default function CheckoutWrapper({
|
|
|
};
|
|
};
|
|
|
const addressFormOnSubmit = async (formData: FullAddressFormData) => {
|
|
const addressFormOnSubmit = async (formData: FullAddressFormData) => {
|
|
|
// console.log('addressFormOnSubmit ---- ',formData); return;
|
|
// console.log('addressFormOnSubmit ---- ',formData); return;
|
|
|
- // 保存完地址之后还要重新请求地址,把保存后的地址id同步到表单里;还需要获取运输方式
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if(addressSaving) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
const saveAddressParam = generateSaveCheckoutAddressParam(formData);
|
|
const saveAddressParam = generateSaveCheckoutAddressParam(formData);
|
|
|
setAddressSaving(true);
|
|
setAddressSaving(true);
|
|
|
try {
|
|
try {
|
|
|
|
|
|
|
|
const res = await saveCheckoutAddress(saveAddressParam);
|
|
const res = await saveCheckoutAddress(saveAddressParam);
|
|
|
console.log('CREATE_CHECKOUT_ADDRESS res ====== ',res);
|
|
console.log('CREATE_CHECKOUT_ADDRESS res ====== ',res);
|
|
|
|
|
+ // 地址保存成功后重新获取运输方式和支付方式
|
|
|
|
|
+ await getShippingMethod();
|
|
|
|
|
+ await getPaymentMethod();
|
|
|
|
|
+
|
|
|
|
|
+ // 保存完地址之后还要重新请求地址,把保存后的地址id同步到表单里;
|
|
|
const queryAddressRes = await getCheckoutAddress();
|
|
const queryAddressRes = await getCheckoutAddress();
|
|
|
if(queryAddressRes !== null) {
|
|
if(queryAddressRes !== null) {
|
|
|
addressForm.resetField('shippingAddressId',{
|
|
addressForm.resetField('shippingAddressId',{
|
|
@@ -182,12 +309,20 @@ export default function CheckoutWrapper({
|
|
|
addressForm.resetField('billingAddressId',{
|
|
addressForm.resetField('billingAddressId',{
|
|
|
defaultValue: queryAddressRes.billingAddress.billingAddressId
|
|
defaultValue: queryAddressRes.billingAddress.billingAddressId
|
|
|
});
|
|
});
|
|
|
- setServerShippingAddress({
|
|
|
|
|
- ...queryAddressRes.shippingAddress
|
|
|
|
|
- });
|
|
|
|
|
- setServerBillingAddress({
|
|
|
|
|
- ...queryAddressRes.billingAddress
|
|
|
|
|
- });
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 同步地址到购物车详情
|
|
|
|
|
+ const newBillingAddress = billingAddressToCartAddress(queryAddressRes.billingAddress);
|
|
|
|
|
+ const newShippingAddress = shippingAddressToCartAddress(queryAddressRes.shippingAddress);
|
|
|
|
|
+ dispatch(updateCart({
|
|
|
|
|
+ billingAddress: newBillingAddress,
|
|
|
|
|
+ shippingAddress: newShippingAddress
|
|
|
|
|
+ }));
|
|
|
|
|
+ setCartData(Object.assign({...cartData},{
|
|
|
|
|
+ billingAddress: {...newBillingAddress},
|
|
|
|
|
+ shippingAddress: {...newShippingAddress}
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
setAddressModalOpen(false);
|
|
setAddressModalOpen(false);
|
|
|
} catch(err) {
|
|
} catch(err) {
|
|
@@ -200,6 +335,175 @@ export default function CheckoutWrapper({
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ const handlePaymentMethodChange = async (method: string) => {
|
|
|
|
|
+ let shippingMethod = '-1';
|
|
|
|
|
+ if(methodShiippingRef.current) {
|
|
|
|
|
+ shippingMethod = methodShiippingRef.current.getSelectShipMethod();
|
|
|
|
|
+ }
|
|
|
|
|
+ overlayLoading.start();
|
|
|
|
|
+ const saveRes = await saveCheckoutCart({
|
|
|
|
|
+ shippingMethod: shippingMethod,
|
|
|
|
|
+ paymentMethod: method,
|
|
|
|
|
+ couponCode: '-1'
|
|
|
|
|
+ });
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ if(!saveRes.error) {
|
|
|
|
|
+ if(saveRes.data) {
|
|
|
|
|
+ const newCartDetail = formatCartDetail(saveRes.data);
|
|
|
|
|
+ dispatch(updateCart(newCartDetail));
|
|
|
|
|
+ setCartData({...newCartDetail});
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showToast(saveRes.msg, 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ };
|
|
|
|
|
+ const handleShippingMethodChange = async (method: string) => {
|
|
|
|
|
+ overlayLoading.start();
|
|
|
|
|
+ const saveRes = await saveCheckoutCart({
|
|
|
|
|
+ shippingMethod: method,
|
|
|
|
|
+ paymentMethod: '-1',
|
|
|
|
|
+ couponCode: '-1'
|
|
|
|
|
+ });
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ if(!saveRes.error) {
|
|
|
|
|
+ if(saveRes.data) {
|
|
|
|
|
+ const newCartDetail = formatCartDetail(saveRes.data);
|
|
|
|
|
+ dispatch(updateCart(newCartDetail));
|
|
|
|
|
+ setCartData({...newCartDetail});
|
|
|
|
|
+ }
|
|
|
|
|
+ // 保存成功之后需要重新获取支付方式
|
|
|
|
|
+ getPaymentMethod();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showToast(saveRes.msg, 'danger');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 校验地址 运输方式,支付方式;
|
|
|
|
|
+ const validateCheckout = async () => {
|
|
|
|
|
+ const addressValid = await addressForm.trigger();
|
|
|
|
|
+
|
|
|
|
|
+ if(!addressValid) {
|
|
|
|
|
+ showToast('Please fill in the address information correctly.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(methodShiippingRef.current) {
|
|
|
|
|
+ const shippingMethodValid = await methodShiippingRef.current.validateShipMethod();
|
|
|
|
|
+ if(!shippingMethodValid) {
|
|
|
|
|
+ showToast('Please select a shipping method.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showToast('Please wait for shipping method loading.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(methodPaymentRef.current) {
|
|
|
|
|
+ const paymentMethodValid = await methodPaymentRef.current.validatePaymentMethod();
|
|
|
|
|
+ const paymentMethod = methodPaymentRef.current.getSelectPaymentMethod();
|
|
|
|
|
+ if(!paymentMethodValid) {
|
|
|
|
|
+ showToast('Please select a payment method.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(paymentMethod === 'airwallex') {
|
|
|
|
|
+ const paymentMethodValid = methodPaymentRef.current.validateAirwallexCard();
|
|
|
|
|
+ return paymentMethodValid;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showToast('Please wait for payment method loading.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(!cartData) {
|
|
|
|
|
+ showToast('Please add product to your cart.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //如果购物车详情没有保存过支付方式,则需要保存支付方式
|
|
|
|
|
+ if(cartData.paymentMethod === null) {
|
|
|
|
|
+ const pmethod = methodPaymentRef.current.getSelectPaymentMethod();
|
|
|
|
|
+ const saveRes = await saveCheckoutCart({
|
|
|
|
|
+ shippingMethod: '-1',
|
|
|
|
|
+ paymentMethod: pmethod,
|
|
|
|
|
+ couponCode: '-1'
|
|
|
|
|
+ });
|
|
|
|
|
+ if(!saveRes.error) {
|
|
|
|
|
+ if(saveRes.data) {
|
|
|
|
|
+ const newCartDetail = formatCartDetail(saveRes.data);
|
|
|
|
|
+ dispatch(updateCart(newCartDetail));
|
|
|
|
|
+ setCartData({...newCartDetail});
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
|
|
+
|
|
|
|
|
+ };
|
|
|
|
|
+ // 苹果支付下单
|
|
|
|
|
+ const applepayPlaceOrder = async () => {
|
|
|
|
|
+ const res = await createOrder();
|
|
|
|
|
+ if(!res.error) {
|
|
|
|
|
+ //创建订单成功后清空redux 购物车
|
|
|
|
|
+ dispatch(clearCart());
|
|
|
|
|
+ return {
|
|
|
|
|
+ orderId: res.data?.gatewayOrderId ?? '',
|
|
|
|
|
+ webOrderId: res.data?.orderId ?? ''
|
|
|
|
|
+ };
|
|
|
|
|
+ } else{
|
|
|
|
|
+ // 抛出错误让第三方支付能够捕获
|
|
|
|
|
+ throw new Error("CREATE_ORDER_ERROR: " + res.msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ // 下单
|
|
|
|
|
+ const handlePlaceOrder: PlaceOrderFunction = async (params) => {
|
|
|
|
|
+ // 校验地址
|
|
|
|
|
+ // overlayLoading.start();
|
|
|
|
|
+ // const validRes = await validateCheckout();
|
|
|
|
|
+ // if(validRes) {
|
|
|
|
|
+ const res = await createOrder(params);
|
|
|
|
|
+ if(res.error) {
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ confirmDialog({
|
|
|
|
|
+ title: "Warning",
|
|
|
|
|
+ content: res.msg + " Create order failed. Please try again.",
|
|
|
|
|
+ noCancel: true,
|
|
|
|
|
+ }).then(() => {});
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //创建订单成功后清空redux 购物车
|
|
|
|
|
+ dispatch(clearCart());
|
|
|
|
|
+ }
|
|
|
|
|
+ const pmethod = methodPaymentRef.current?.getSelectPaymentMethod();
|
|
|
|
|
+ const resultData:PlaceOrderResult<keyof ExtraMapPlaceOrderResult> = {
|
|
|
|
|
+ // valid: true,
|
|
|
|
|
+ data: res.data,
|
|
|
|
|
+ otherData: null
|
|
|
|
|
+ }
|
|
|
|
|
+ if(pmethod === 'airwallex' && methodPaymentRef.current) {
|
|
|
|
|
+ const airwallexCardElements = methodPaymentRef.current.getAirwallexCardElements();
|
|
|
|
|
+ resultData.otherData = airwallexCardElements.cardNumber as Payment.CardNumberElementType;
|
|
|
|
|
+ }
|
|
|
|
|
+ return resultData;
|
|
|
|
|
+ // } else {
|
|
|
|
|
+ // overlayLoading.stop();
|
|
|
|
|
+ // return {valid: false, data: null, otherData: null};
|
|
|
|
|
+ // }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const hasAfterpay = paymentMethodDatas.some(x=>x.method==="afterpay");
|
|
|
|
|
+
|
|
|
|
|
+ if(hasAfterpay){
|
|
|
|
|
+ loadAfterpayScript();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [paymentMethodDatas]);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
// function tt() {
|
|
// function tt() {
|
|
|
// const arr = [
|
|
// const arr = [
|
|
|
// ['US','3803800217'],
|
|
// ['US','3803800217'],
|
|
@@ -223,36 +527,109 @@ export default function CheckoutWrapper({
|
|
|
<section className="w-full">
|
|
<section className="w-full">
|
|
|
<div>
|
|
<div>
|
|
|
{/* <button className="w-25 h-8 bg-amber-500 flex items-center justify-center" onClick={tt}>test</button> */}
|
|
{/* <button className="w-25 h-8 bg-amber-500 flex items-center justify-center" onClick={tt}>test</button> */}
|
|
|
- {loadingData ? <AddressResultDisplayLoading />
|
|
|
|
|
- :<AddressResultDisplay shippingAddress={serverShippingAddress}
|
|
|
|
|
|
|
+ {/* {loadingAddress ?
|
|
|
|
|
+ <AddressResultDisplayLoading />
|
|
|
|
|
+ :
|
|
|
|
|
+ <AddressResultDisplay shippingAddress={cartData.shippingAddress || null}
|
|
|
|
|
+ onAddressModalOpenClick={openAddressModal}
|
|
|
|
|
+ />
|
|
|
|
|
+ } */}
|
|
|
|
|
+ <AddressResultDisplay shippingAddress={cartData.shippingAddress || null}
|
|
|
onAddressModalOpenClick={openAddressModal}
|
|
onAddressModalOpenClick={openAddressModal}
|
|
|
- />}
|
|
|
|
|
- <CheckoutAddressModal
|
|
|
|
|
|
|
+ />
|
|
|
|
|
+ <CommonModal
|
|
|
isOpen={addressModalOpen}
|
|
isOpen={addressModalOpen}
|
|
|
onClose={closeAddressModal}
|
|
onClose={closeAddressModal}
|
|
|
|
|
+ header={
|
|
|
|
|
+ <div className="w-full box-border p-3.75 h-14.5 flex justify-between items-center">
|
|
|
|
|
+ <span>Shipping Address</span>
|
|
|
|
|
+ <button className="w-6 h-6" onClick={() => closeAddressModal(false)}>
|
|
|
|
|
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" className="h-6 transition-all ease-in-out hover:scale-110"><path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12"></path></svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
body={
|
|
body={
|
|
|
- <>
|
|
|
|
|
- <FormProvider {...addressForm}>
|
|
|
|
|
- <form>
|
|
|
|
|
- <ShippingAddressCheckout countries={countries} />
|
|
|
|
|
- <BillingAddressCheckout countries={countries} />
|
|
|
|
|
- </form>
|
|
|
|
|
- </FormProvider>
|
|
|
|
|
- </>
|
|
|
|
|
|
|
+ <div className="box-border w-full h-full p-3.75">
|
|
|
|
|
+ <p className="text-ly-12 leading-ly-20">
|
|
|
|
|
+ Select a billing address from your address book or enter a new address.
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <FormProvider {...addressForm}>
|
|
|
|
|
+ <form>
|
|
|
|
|
+ <ShippingAddressCheckout />
|
|
|
|
|
+ <BillingAddressCheckout />
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </FormProvider>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
}
|
|
}
|
|
|
footer={
|
|
footer={
|
|
|
|
|
+ <div className="box-border w-full p-3.75">
|
|
|
<button className="block flex justify-center items-center w-full h-12 bg-ly-green rounded-3xl text-white text-ly-16 font-bold"
|
|
<button className="block flex justify-center items-center w-full h-12 bg-ly-green rounded-3xl text-white text-ly-16 font-bold"
|
|
|
onClick={addressForm.handleSubmit(addressFormOnSubmit)}
|
|
onClick={addressForm.handleSubmit(addressFormOnSubmit)}
|
|
|
>
|
|
>
|
|
|
{addressSaving ? <LoadingSpinner /> : 'Save'}
|
|
{addressSaving ? <LoadingSpinner /> : 'Save'}
|
|
|
</button>
|
|
</button>
|
|
|
|
|
+ </div>
|
|
|
}
|
|
}
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
<div className="mt-6 box-border px-4">
|
|
<div className="mt-6 box-border px-4">
|
|
|
- {/* <CheckoutShippingMethod /> */}
|
|
|
|
|
|
|
+ <h3 className="text-ly-24 font-medium">Shipping Method</h3>
|
|
|
|
|
+ {shippingMethodLoading ?
|
|
|
|
|
+
|
|
|
|
|
+ <CheckoutShippingMethodLoading />
|
|
|
|
|
+ :
|
|
|
|
|
+ <ShippingMethodCheckout
|
|
|
|
|
+ ref={methodShiippingRef}
|
|
|
|
|
+ shipMethods={shippingMethodDatas}
|
|
|
|
|
+ errorMsg={shippingMethodError}
|
|
|
|
|
+ selectedShippingRate={selectedShippingRate}
|
|
|
|
|
+ onShipMethodChange={handleShippingMethodChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <div className="mt-6 box-border px-4">
|
|
|
|
|
+ <h3 className="text-ly-24 font-medium">Payment Method</h3>
|
|
|
|
|
+ { paymentMethodLoading && <LoadingPaymentMethod /> }
|
|
|
|
|
+ { (!paymentMethodLoading && paymentMethodError) && <p className="mt-3 text-red-500 text-ly-12">{paymentMethodError}</p>}
|
|
|
|
|
+ { (!paymentMethodLoading && !paymentMethodError) &&
|
|
|
|
|
+ <PaymentMethodCheckout
|
|
|
|
|
+ ref={methodPaymentRef}
|
|
|
|
|
+ paymentMethods={paymentMethodDatas}
|
|
|
|
|
+ selectedPaymentMethod={selectedPaymentMethod}
|
|
|
|
|
+ onPaymentMethodChange={handlePaymentMethodChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="mt-3 box-border px-4 pb-8">
|
|
|
|
|
+ <p className="text-ly-12 text-[#666666] leading-ly-20">By providing your information, you agree to Wiggins'sPrivacy Policyand Terms of Use.</p>
|
|
|
|
|
+ <div className="mt-6 w-full">
|
|
|
|
|
+ { paymentMethodLoading ?
|
|
|
|
|
+ <LoadingCheckoutPlaceOrder />
|
|
|
|
|
+ :
|
|
|
|
|
+ (selectedPaymentMethod === 'applepay' ?
|
|
|
|
|
+ <PaypalApplepayButton
|
|
|
|
|
+ grandTotal={cartData.grandTotal}
|
|
|
|
|
+ billingContact={cartData.billingAddress}
|
|
|
|
|
+ shippingContact={cartData.shippingAddress}
|
|
|
|
|
+ validateCheckout={validateCheckout}
|
|
|
|
|
+ createOrder={applepayPlaceOrder}
|
|
|
|
|
+ />
|
|
|
|
|
+ :
|
|
|
|
|
+ <CheckoutPlaceOrder
|
|
|
|
|
+ paymentMethod={selectedPaymentMethod}
|
|
|
|
|
+ validateCheckout={validateCheckout}
|
|
|
|
|
+ clickPlaceOrder={handlePlaceOrder}
|
|
|
|
|
+ />)
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
</section>
|
|
</section>
|
|
|
|
|
|
|
|
);
|
|
);
|