|
@@ -1,8 +1,12 @@
|
|
|
"use client";
|
|
"use client";
|
|
|
|
|
|
|
|
import Image from "next/image";
|
|
import Image from "next/image";
|
|
|
-import { use } from "react";
|
|
|
|
|
-import { useForm, get } from "react-hook-form";
|
|
|
|
|
|
|
+import { use,useState } from "react";
|
|
|
|
|
+import { useForm, get, useWatch } from "react-hook-form";
|
|
|
|
|
+import { useRouter } from 'next/navigation'
|
|
|
|
|
+import { useCustomToast } from "@/utils/hooks/useToast";
|
|
|
|
|
+import AirwallexCardInput from "../PaymentMethodAdditional/AirwallexCardInput";
|
|
|
|
|
+import {useAirwallexCard} from "@/lib/Airwallex/useAirwallexCard";
|
|
|
import {RepayOrderPaymentMethodsData} from "@/types/checkout/type";
|
|
import {RepayOrderPaymentMethodsData} from "@/types/checkout/type";
|
|
|
import {GraphqlRequestResult} from "@/types/graphqlFetch/type";
|
|
import {GraphqlRequestResult} from "@/types/graphqlFetch/type";
|
|
|
import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
|
|
import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
|
|
@@ -10,6 +14,7 @@ import PaypalButton from "../PaymentButton/PaypalButton";
|
|
|
import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
|
|
import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
|
|
|
import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
|
|
import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
|
|
|
|
|
|
|
|
|
|
+/**@todo 优化目标:不使用 react-hook-form */
|
|
|
export function PaymentMethodContinueTpPay({
|
|
export function PaymentMethodContinueTpPay({
|
|
|
paymentMethodsDataPromise,
|
|
paymentMethodsDataPromise,
|
|
|
defaultPaymentMethod,
|
|
defaultPaymentMethod,
|
|
@@ -19,14 +24,24 @@ export function PaymentMethodContinueTpPay({
|
|
|
defaultPaymentMethod: {method: string; methodTitle: string;}
|
|
defaultPaymentMethod: {method: string; methodTitle: string;}
|
|
|
orderId: string;
|
|
orderId: string;
|
|
|
}) {
|
|
}) {
|
|
|
|
|
+ const router = useRouter();
|
|
|
|
|
+ const { showToast } = useCustomToast();
|
|
|
const paymentMethodsData = use(paymentMethodsDataPromise);
|
|
const paymentMethodsData = use(paymentMethodsDataPromise);
|
|
|
const paymentMethods = paymentMethodsData.data?.collectionRepayOrderPaymentMethods || [];
|
|
const paymentMethods = paymentMethodsData.data?.collectionRepayOrderPaymentMethods || [];
|
|
|
|
|
|
|
|
const { paymentRepay } = usePlaceOrder();
|
|
const { paymentRepay } = usePlaceOrder();
|
|
|
|
|
+
|
|
|
|
|
+ const {airwallexCardState,getCardElements} = useAirwallexCard(true,{
|
|
|
|
|
+ cardNumber: 'airwallex_cardNumber',
|
|
|
|
|
+ cvc: 'airwallex_cvc',
|
|
|
|
|
+ expiry: 'airwallex_expiry'
|
|
|
|
|
+ });
|
|
|
|
|
+ const [isPay, setIsPay] = useState(false);
|
|
|
|
|
|
|
|
const {
|
|
const {
|
|
|
register,
|
|
register,
|
|
|
- watch,
|
|
|
|
|
|
|
+ // watch,
|
|
|
|
|
+ control,
|
|
|
formState: { errors }
|
|
formState: { errors }
|
|
|
} = useForm({
|
|
} = useForm({
|
|
|
mode: "onChange",
|
|
mode: "onChange",
|
|
@@ -38,19 +53,45 @@ export function PaymentMethodContinueTpPay({
|
|
|
|
|
|
|
|
// 参考 https://github.com/react-hook-form/error-message/blob/master/src/ErrorMessage.tsx
|
|
// 参考 https://github.com/react-hook-form/error-message/blob/master/src/ErrorMessage.tsx
|
|
|
const paymentMethodError = get(errors, "paymentMethod");
|
|
const paymentMethodError = get(errors, "paymentMethod");
|
|
|
- const selectedPaymentMethod = watch("paymentMethod");
|
|
|
|
|
|
|
+ // react 19中使用watch 会提示React Hook Form's `useForm()` API returns a `watch()` function which cannot be memoized safely.
|
|
|
|
|
+ // const selectedPaymentMethod = watch("paymentMethod");
|
|
|
|
|
+ const selectedPaymentMethod = useWatch({
|
|
|
|
|
+ control,
|
|
|
|
|
+ name:"paymentMethod",
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const handleCreateOrder = async () => {
|
|
|
|
|
- return await paymentRepay({
|
|
|
|
|
|
|
+ const paypalRepayOrder = async () => {
|
|
|
|
|
+ overlayLoading.start();
|
|
|
|
|
+ const res = await paymentRepay({
|
|
|
orderId: Number(orderId),
|
|
orderId: Number(orderId),
|
|
|
paymentMethod: selectedPaymentMethod
|
|
paymentMethod: selectedPaymentMethod
|
|
|
});
|
|
});
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ if(!res.error && res.data !== null) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ error: false,
|
|
|
|
|
+ msg: '',
|
|
|
|
|
+ orderId: res.data.gatewayOrderId,
|
|
|
|
|
+ webOrderId: res.data.orderId
|
|
|
|
|
+ };
|
|
|
|
|
+ } else {
|
|
|
|
|
+ confirmDialog({
|
|
|
|
|
+ title: "Warning",
|
|
|
|
|
+ content: res.msg + " Create order failed. Please try again.",
|
|
|
|
|
+ noCancel: true,
|
|
|
|
|
+ }).then(() => {});
|
|
|
|
|
+ return {
|
|
|
|
|
+ error: true,
|
|
|
|
|
+ msg: '',
|
|
|
|
|
+ orderId: '',
|
|
|
|
|
+ webOrderId: ''
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
};
|
|
};
|
|
|
- const handleClickPay = async () => {
|
|
|
|
|
- return true;
|
|
|
|
|
- };
|
|
|
|
|
|
|
+
|
|
|
const airwallexRepay = async () => {
|
|
const airwallexRepay = async () => {
|
|
|
|
|
+ setIsPay(true);
|
|
|
overlayLoading.start();
|
|
overlayLoading.start();
|
|
|
const res = await paymentRepay({
|
|
const res = await paymentRepay({
|
|
|
orderId: Number(orderId),
|
|
orderId: Number(orderId),
|
|
@@ -58,9 +99,9 @@ export function PaymentMethodContinueTpPay({
|
|
|
paymentSuccessUrl: window.location.origin + '/paymentresult/result'
|
|
paymentSuccessUrl: window.location.origin + '/paymentresult/result'
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- if(!res.error && res.orderId) {
|
|
|
|
|
|
|
+ if(!res.error && res.data !== null) {
|
|
|
// 创建订单成功了,跳转支付页面
|
|
// 创建订单成功了,跳转支付页面
|
|
|
- window.location.href = res.orderId;
|
|
|
|
|
|
|
+ window.location.href = res.data.gatewayOrderId;
|
|
|
} else {
|
|
} else {
|
|
|
//创建订单出错了
|
|
//创建订单出错了
|
|
|
overlayLoading.stop();
|
|
overlayLoading.stop();
|
|
@@ -70,17 +111,100 @@ export function PaymentMethodContinueTpPay({
|
|
|
noCancel: true,
|
|
noCancel: true,
|
|
|
}).then(() => {});
|
|
}).then(() => {});
|
|
|
}
|
|
}
|
|
|
|
|
+ setIsPay(false);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ const validateAirwallexCard = () => {
|
|
|
|
|
+
|
|
|
|
|
+ if(!airwallexCardState.cardNumberReady) {
|
|
|
|
|
+ showToast('Card number input has not ready. Please wait a minute.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(!airwallexCardState.expiryReady) {
|
|
|
|
|
+ showToast('Card expiry date input has not ready. Please wait a minute.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(!airwallexCardState.cvcReady) {
|
|
|
|
|
+ showToast('Card cvc input has not ready. Please wait a minute.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!airwallexCardState.cardNumberValid) {
|
|
|
|
|
+ showToast('Please input your card number.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(!airwallexCardState.cvcValid) {
|
|
|
|
|
+ showToast('Please input cvc.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(!airwallexCardState.expiryValid) {
|
|
|
|
|
+ showToast('Please input expiry date.', 'danger');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+ const cardAirwallexRepay = async () => {
|
|
|
|
|
+ setIsPay(true);
|
|
|
|
|
+ overlayLoading.start();
|
|
|
|
|
+ const validRes = validateAirwallexCard();
|
|
|
|
|
+ if(validRes) {
|
|
|
|
|
+
|
|
|
|
|
+ const res = await paymentRepay({
|
|
|
|
|
+ orderId: Number(orderId),
|
|
|
|
|
+ paymentMethod: selectedPaymentMethod,
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if(!res.error && res.data !== null) {
|
|
|
|
|
+ // 创建订单成功
|
|
|
|
|
+ const cardNumberElement = getCardElements().cardNumber;
|
|
|
|
|
+ const paymentIntentIdClientSecret = res.data.gatewayOrderId.split(':');
|
|
|
|
|
+ const paymentIntentId = paymentIntentIdClientSecret[0];
|
|
|
|
|
+ const paymentIntentClientSecret = paymentIntentIdClientSecret[1];
|
|
|
|
|
+
|
|
|
|
|
+ cardNumberElement?.confirm({
|
|
|
|
|
+ intent_id: paymentIntentId,
|
|
|
|
|
+ client_secret: paymentIntentClientSecret,
|
|
|
|
|
+ }).then((result) => {
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ console.log('cardNumberElement.confirm result ---- ', result);
|
|
|
|
|
+ // 支付成功 跳转页面
|
|
|
|
|
+ router.replace('/paymentresult/result?orderId=' + orderId + '&return_result=success');
|
|
|
|
|
+ }).catch((error) => {
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ console.log('cardNumberElement.confirm error ---- ', error);
|
|
|
|
|
+ // 支付失败
|
|
|
|
|
+ router.replace('/paymentresult/result?orderId=' + orderId + '&return_result=failure');
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //创建订单出错了
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ confirmDialog({
|
|
|
|
|
+ title: "Warning",
|
|
|
|
|
+ content: res.msg + " Create order failed. Please try again.",
|
|
|
|
|
+ noCancel: true,
|
|
|
|
|
+ }).then(() => {});
|
|
|
|
|
+ }
|
|
|
|
|
+ }else {
|
|
|
|
|
+ overlayLoading.stop();
|
|
|
|
|
+ }
|
|
|
|
|
+ setIsPay(false);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const airwallexCardElementShow = selectedPaymentMethod === 'airwallex' && airwallexCardState.cardNumberReady && airwallexCardState.expiryReady && airwallexCardState.cvcReady;
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
|
<form name="shipping method" className="w-full box-border px-4 mt-4">
|
|
<form name="shipping method" className="w-full box-border px-4 mt-4">
|
|
|
<div className="mt-3 w-full">
|
|
<div className="mt-3 w-full">
|
|
|
{paymentMethodsData.error && <p className="text-red-500 text-ly-12">{paymentMethodsData.error}</p>}
|
|
{paymentMethodsData.error && <p className="text-red-500 text-ly-12">{paymentMethodsData.error}</p>}
|
|
|
|
|
+ <ul className="w-full">
|
|
|
{
|
|
{
|
|
|
paymentMethods.map((item) => {
|
|
paymentMethods.map((item) => {
|
|
|
- return (
|
|
|
|
|
- <label htmlFor={"paymentmethod-" + item.method} key={item.method}
|
|
|
|
|
- className="group box-border flex items-center relative bg-ly-lightgray mt-3 p-3 rounded-sm has-checked:bg-white has-checked:border-1 has-checked:border-ly-green"
|
|
|
|
|
|
|
+ return (<li className="w-full mt-3" key={item.method}>
|
|
|
|
|
+ <label htmlFor={"paymentmethod-" + item.method}
|
|
|
|
|
+ className="group box-border flex items-center relative bg-ly-lightgray p-3 rounded-sm has-checked:bg-white has-checked:border-1 has-checked:border-ly-green"
|
|
|
>
|
|
>
|
|
|
<p className="pr-2 flex-none relative">
|
|
<p className="pr-2 flex-none relative">
|
|
|
<Image className="block h-8 w-auto"
|
|
<Image className="block h-8 w-auto"
|
|
@@ -103,10 +227,22 @@ export function PaymentMethodContinueTpPay({
|
|
|
required: 'Please select a shipping method.',
|
|
required: 'Please select a shipping method.',
|
|
|
})}
|
|
})}
|
|
|
/>
|
|
/>
|
|
|
- </label>
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ </label>
|
|
|
|
|
+ { item.method === "airwallex" &&
|
|
|
|
|
+ <div className="w-full mt-3"
|
|
|
|
|
+ style={{display: airwallexCardElementShow ? 'block' : 'none'}}
|
|
|
|
|
+ >
|
|
|
|
|
+ <AirwallexCardInput
|
|
|
|
|
+ cardNumberErrorMsg={airwallexCardState.cardNumberErrorMsg}
|
|
|
|
|
+ expiryErrorMsg={airwallexCardState.expiryErrorMsg}
|
|
|
|
|
+ cvcErrorMsg={airwallexCardState.cvcErrorMsg}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ </li>)
|
|
|
})
|
|
})
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ </ul>
|
|
|
|
|
|
|
|
{paymentMethodError && <p className="text-red-500 text-ly-12">{paymentMethodError.message}</p>}
|
|
{paymentMethodError && <p className="text-red-500 text-ly-12">{paymentMethodError.message}</p>}
|
|
|
</div>
|
|
</div>
|
|
@@ -120,16 +256,24 @@ export function PaymentMethodContinueTpPay({
|
|
|
<div className="mt-6 w-full">
|
|
<div className="mt-6 w-full">
|
|
|
{selectedPaymentMethod === 'paypal_smart_button' &&
|
|
{selectedPaymentMethod === 'paypal_smart_button' &&
|
|
|
<PaypalButton
|
|
<PaypalButton
|
|
|
- createOrder={handleCreateOrder}
|
|
|
|
|
- onPaymentClick={handleClickPay}
|
|
|
|
|
|
|
+ createOrder={paypalRepayOrder}
|
|
|
isRePay={true}
|
|
isRePay={true}
|
|
|
/>
|
|
/>
|
|
|
}
|
|
}
|
|
|
{(selectedPaymentMethod === 'awxklarna' || selectedPaymentMethod === 'awxafterpay') &&
|
|
{(selectedPaymentMethod === 'awxklarna' || selectedPaymentMethod === 'awxafterpay') &&
|
|
|
<button className="flex items-center justify-center w-full h-12 bg-ly-green text-white rounded-3xl text-ly-16 font-bold"
|
|
<button className="flex items-center justify-center w-full h-12 bg-ly-green text-white rounded-3xl text-ly-16 font-bold"
|
|
|
onClick={airwallexRepay}
|
|
onClick={airwallexRepay}
|
|
|
|
|
+ disabled={isPay}
|
|
|
|
|
+ >
|
|
|
|
|
+ Pay
|
|
|
|
|
+ </button>
|
|
|
|
|
+ }
|
|
|
|
|
+ {selectedPaymentMethod === 'airwallex' &&
|
|
|
|
|
+ <button className="flex items-center justify-center w-full h-12 bg-ly-green text-white rounded-3xl text-ly-16 font-bold"
|
|
|
|
|
+ onClick={cardAirwallexRepay}
|
|
|
|
|
+ disabled={isPay}
|
|
|
>
|
|
>
|
|
|
- Place Order
|
|
|
|
|
|
|
+ Pay
|
|
|
</button>
|
|
</button>
|
|
|
}
|
|
}
|
|
|
|
|
|