| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- "use client";
- import { type Ref, useEffect, useImperativeHandle } from "react";
- import Link from "next/link";
- import clsx from "clsx";
- import { useForm, get } from "react-hook-form";
- import { usePayPalMessages } from "@paypal/react-paypal-js/sdk-v6";
- import {useStore} from "@/utils/hooks/useConfig";
- import {currencyCountryMap} from "@/utils/constants";
- import {getPaymentMethodLogo,getPaymentDescription} from "@/utils/paymentTools";
- import type { CheckoutPaymentMethod } from "@/types/checkout/type";
- import AirwallexCardInput from "./PaymentMethodAdditional/AirwallexCardInput";
- import {useAirwallexCardState} from "@/lib/Airwallex/useAirwallexCard";
- /**
- * 关于如何确定默认选中哪个支付方式:
- * 从购物车详情中获取支付方式 A,支付方式列表中有A,那么把A标记为默认选中;如果支付方式列表中没有A,把paypal标记为默认选中;
- */
- export interface RefPaymentMethodsHandle {
- getSelectPaymentMethod: () => string;
- getSelectPaymentTitle: () => string;
- validatePaymentMethod: () => Promise<boolean>;
- }
- export function PaymentMethodCheckout({
- ref,
- paymentMethods,
- selectedPaymentMethod,
- grandTotal,
- onPaymentMethodChange,
- }: {
- ref: Ref<RefPaymentMethodsHandle>;
- paymentMethods: CheckoutPaymentMethod[];
- selectedPaymentMethod: string;
- grandTotal: number;
- onPaymentMethodChange: (value: string) => Promise<boolean>;
- }) {
- const { error:paypalMessageError } = usePayPalMessages({});
- const {store} = useStore();
- const currencySymbol = currencyCountryMap[store.currentCurrency].symbol;
- const airwallexCardState = useAirwallexCardState();
-
- const {
- register,
- getValues,
- trigger,
- reset,
- formState: { errors }
- } = useForm({
- mode: "onChange",
- reValidateMode: "onChange",
- // defaultValues: {
- // paymentMethod: defaultValue,
- // }
- });
- // 参考 https://github.com/react-hook-form/error-message/blob/master/src/ErrorMessage.tsx
- const paymentMethodError = get(errors, "paymentMethod");
- const paymentMethodOnChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
- onPaymentMethodChange(event.target.value);
- };
- useEffect(() => {
- let defaultValue = '';
-
- const findItem = paymentMethods.find((item) => item.method === selectedPaymentMethod);
- if(findItem !== undefined) {
- defaultValue = selectedPaymentMethod;
- } else {
- paymentMethods.forEach((item) => {
- if(item.method === 'paypal_smart_button') {
- defaultValue = item.method;
- }
- });
- }
- reset({ paymentMethod: defaultValue });
- },[paymentMethods,selectedPaymentMethod]);
- useImperativeHandle(ref, () => {
- return {
- // 获取用户选择的支付方式
- getSelectPaymentMethod: () => {
- return getValues('paymentMethod');
- },
- getSelectPaymentTitle: () => {
- const nowValue = getValues('paymentMethod');
- const findItem = paymentMethods.find((item) => item.method === nowValue);
- return findItem?.title ?? '';
- },
- // 触发校验
- validatePaymentMethod: () => {
- return trigger();
- }
- }
-
- },[paymentMethods]);
- const airwallexOtherInfoShow = selectedPaymentMethod === 'airwallex' && airwallexCardState.cardNumberReady && airwallexCardState.expiryReady && airwallexCardState.cvcReady;
- const genetePaymentTips = (payment: string) => {
- return getPaymentDescription({
- payment: payment,
- grandTotal: grandTotal,
- symble: currencySymbol
- })
- };
- return (<>
-
- <form name="shipping method" className="w-full">
-
- <div className="mt-4 w-full">
- <ul className="w-full">
- {
- paymentMethods.map((item) => {
- return (
- <li key={item.method}
- className={clsx("w-full mt-3 first:mt-0",
- selectedPaymentMethod === 'airwallex' && item.method === 'airwallex' && ['bg-[#ECF7F4] border-1 border-ly-green rounded-xs pb-3.5']
- )}
- >
- <label htmlFor={"paymentmethod-" + item.method}
- className={clsx(
- "group box-border flex items-center relative px-4 py-3.5 bg-ly-lightgray",
- selectedPaymentMethod !== 'airwallex' && "has-checked:bg-[#ECF7F4] has-checked:border-1 has-checked:border-ly-green has-checked:rounded-xs",
- selectedPaymentMethod === 'airwallex' && "has-checked:bg-[#ECF7F4]"
- )}
- >
- <p className="mr-2 flex-none relative">
- <img
- className="block h-6 w-auto"
- src={getPaymentMethodLogo(item.method)}
- alt={`Payment Method ${item.title}`}
- />
- </p>
- <p className="text-ly-14 text-[#666666]">
- {genetePaymentTips(item.method)}
- </p>
- <div className="absolute top-1/2 -translate-y-1/2 right-4 flex items-center">
- <svg className="check w-4 h-4 hidden group-has-[:checked]:block" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="1" width="14" height="14" rx="1" fill="#000000" ></rect><path stroke="rgba(255, 255, 255, 1)" strokeWidth="1.5" strokeLinecap="square" d="M5.3335 8L7.3335 10L11.3335 6"></path></svg>
- <svg className="uncheck w-4 h-4 block group-has-[:checked]:hidden" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="1" width="14" height="14" rx="1" fill="#FFFFFF" ></rect><path fill="rgba(0, 0, 0, 1)" d="M2 0.5L14 0.5C14.8284 0.5 15.5 1.17157 15.5 2L15.5 14C15.5 14.8284 14.8284 15.5 14 15.5L2 15.5C1.17157 15.5 0.5 14.8284 0.5 14L0.5 2C0.5 1.17157 1.17157 0.5 2 0.5ZM2 1.5C1.72386 1.5 1.5 1.72386 1.5 2L1.5 14C1.5 14.2761 1.72386 14.5 2 14.5L14 14.5C14.2761 14.5 14.5 14.2761 14.5 14L14.5 2C14.5 1.72386 14.2761 1.5 14 1.5L2 1.5Z"></path></svg>
- </div>
- <input id={"paymentmethod-" + item.method}
- className="absolute w-6 h-6 opacity-0"
- type="radio"
- value={item.method}
- {...register('paymentMethod', {
- required: 'Please select a payment method.',
- onChange: (event) => {
- paymentMethodOnChange(event);
- },
- })}
- />
- </label>
- { item.method === "airwallex" &&
- <div className="w-full px-4"
- style={{display: airwallexOtherInfoShow ? 'block' : 'none'}}
- >
- <AirwallexCardInput />
- </div>
- }
- </li>
- )
- })
- }
- </ul>
-
- {paymentMethodError && <p className="text-red-500 text-ly-12">{paymentMethodError.message}</p>}
- </div>
- <p className="text-ly-14 mt-3 leading-ly-20">
- By placing this order, I agree to the <Link href="/return-policy">Return Policy</Link> and the <Link href="/terms-conditions">Terms & Conditions</Link>.
- </p>
- {!paypalMessageError &&
- <div className="mt-4">
- <paypal-message className="checkout-paypal-message"
- auto-bootstrap={true}
- amount={String(grandTotal)}
- currency-code={store.currentCurrency}
- // buyer-country="US" //这个属性是沙盒环境测试用的,正式环境不要设置这个参数
- />
- </div>
- }
-
- </form>
- </>);
-
- }
|