import { useCallback, useMemo } from 'react'
import { __ } from '@wordpress/i18n'
import apiFetch from '@wordpress/api-fetch'
import classNames from 'classnames'
import { useForm } from '../../components/Form/FormProvider'
import { IFormContext } from '../../components/Form/types'
import { Validators, validateField } from '../../components/Form/validation'
import { useBookingContext } from '../../providers/BookingFormProvider/BookingFormProvider'
import { stripeMethods } from '../BookingForm/PaymentHandler/payments/Stripe/StripeMethods'
import {
GiftCardPaymentSection,
} from './GiftCardPaymentSection'
import { GiftCardPaymentHandler } from './GiftCardPaymentHandler'
import { GiftCardThankYou, IGiftCardResult } from './GiftCardThankYou'
import { GiftCardPaymentSuccess } from './GiftCardPaymentSuccess'
import { PaymentCancelled } from '../BookingForm/LandingPages/PaymentCancelled/PaymentCancelled'
import { GiftCardAmountStep } from './GiftCardAmountStep'
import { GiftCardDesignStep } from './GiftCardDesignStep'
import { GiftCardRecipientStep } from './GiftCardRecipientStep'
import { GiftCardSendTimeStep } from './GiftCardSendTimeStep'
import { GiftCardBuyerStep } from './GiftCardBuyerStep'
import { GiftCardCheckoutBar } from './GiftCardCheckoutBar'
import { GiftCardContentProps } from './types'
import {
formatGiftCardSendDateTime,
getGiftCardPresetAmounts,
getGiftCardPolicyHtml,
giftCardSendOnPayload,
isGiftCardCustomAmountAllowed,
} from './utils'
export const GiftCardContent = ({
fields,
sendOn,
setSendOn,
sendOnError,
setSendOnError,
sendOnTouched,
setSendOnTouched,
selectedMethod,
setSelectedMethod,
paymentMethods,
formattedAmount,
amountValue,
buyerEmail,
isNarrowForm,
validateSendOn,
sendTime,
setSendTime,
sendTimezone,
setSendTimezone,
stripeDetails,
setStripeDetails,
purchasePhase,
setPurchasePhase,
purchaseResult,
setPurchaseResult,
paymentUrl,
setPaymentUrl,
purchaseError,
setPurchaseError,
purchasing,
setPurchasing,
paypalReturnPaymentId,
}: GiftCardContentProps) => {
const { anyTouched, setAnyTouched } = useForm() as IFormContext
const { stripeObj, preset, priceFormat } = useBookingContext()
const presetAmounts = useMemo(
() => getGiftCardPresetAmounts(preset),
[preset]
)
const allowCustom = isGiftCardCustomAmountAllowed(preset)
const policyHtml = getGiftCardPolicyHtml(preset)
const sendTiming = String(
fields.find((field) => field.slug === 'send_timing')?.value ?? 'now'
)
const recipientName = useMemo(() => {
const field = fields.find((field) => field.slug === 'recipient_name')
return String(field?.value ?? '')
}, [fields])
const isFormValid = useMemo(() => {
const fieldErrors = fields.some((field) => {
const validators = [
...(field.required ? [Validators.required] : []),
...(field.type === 'email' ? [Validators.email] : []),
...(field.validators || []),
]
return validateField({ ...field, validators } as any).length > 0
})
const nextSendOnError =
sendTiming === 'later'
? validateSendOn(sendOn, sendTime, sendTimezone)
: null
return !fieldErrors && !nextSendOnError && !!selectedMethod
}, [
fields,
sendTiming,
sendOn,
sendTime,
sendTimezone,
selectedMethod,
validateSendOn,
])
const isStripeMethod = stripeMethods.includes(selectedMethod)
const isWalletMethod = ['google_pay', 'apple_pay'].includes(selectedMethod)
const isStripeReady = useMemo(() => {
if (!isStripeMethod) {
return true
}
if (!stripeDetails?.client_secret || stripeDetails?.error) {
return false
}
if (isWalletMethod) {
return true
}
return !!stripeObj?.isValidated
}, [isStripeMethod, isWalletMethod, stripeDetails, stripeObj?.isValidated])
const canPurchase = isFormValid && isStripeReady && !!selectedMethod
const buildPayload = useCallback(() => {
const getValue = (slug: string) =>
fields.find((field) => field.slug === slug)?.value
return {
buyer_name: String(getValue('buyer_name') ?? ''),
buyer_email: String(getValue('buyer_email') ?? ''),
recipient_name: String(getValue('recipient_name') ?? ''),
recipient_email: String(getValue('recipient_email') ?? ''),
message: String(getValue('message') ?? ''),
design: String(getValue('design') ?? ''),
amount: amountValue,
send_on: giftCardSendOnPayload(sendTiming, sendOn, sendTime),
send_timezone: sendTiming === 'later' ? sendTimezone : '',
expiration_date: '',
applicable_for_all_services: 'yes',
services: [],
payment_method: selectedMethod,
stripe_details: stripeDetails
? {
intent_id: stripeDetails.intent_id,
client_secret: stripeDetails.client_secret,
}
: undefined,
}
}, [
fields,
amountValue,
sendTiming,
sendOn,
sendTime,
sendTimezone,
selectedMethod,
stripeDetails,
])
const completeStripePurchase = useCallback(async () => {
const { stripe, elements } = stripeObj || {}
if (!stripe || !elements) {
setPurchaseError(
__(
'Stripe is not ready. Please try again.',
'webba-booking-lite'
)
)
return false
}
const redirectUrl =
preset?.settings?.stripe_redirect_url || window.location.href
const { error, paymentIntent } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `${redirectUrl}?wbk_gift_card=1&paymentMethod=${selectedMethod}&`,
payment_method_data: {
billing_details: {
email: buyerEmail || undefined,
},
},
},
redirect: 'if_required',
})
if (error) {
setPurchaseError(
error.message ||
__('Payment failed. Please try again.', 'webba-booking-lite')
)
return false
}
if (paymentIntent?.status !== 'succeeded') {
setPurchaseError(
__('Payment was not completed.', 'webba-booking-lite')
)
return false
}
const payload = {
...buildPayload(),
payment_intent_id: paymentIntent.id,
stripe_details: {
intent_id: paymentIntent.id,
},
}
const response = await apiFetch<{
success?: boolean
message?: string
gift_card?: IGiftCardResult
payment_method?: string
}>({
path: '/wbk/v2/purchase-gift-card/',
method: 'POST',
data: payload,
})
if (!response?.success || !response.gift_card) {
setPurchaseError(
response?.message ||
__(
'Unable to complete gift card purchase.',
'webba-booking-lite'
)
)
return false
}
setPurchaseResult(response.gift_card)
setPurchasePhase('success')
return true
}, [
stripeObj,
preset,
selectedMethod,
buyerEmail,
buildPayload,
setPurchaseError,
setPurchaseResult,
setPurchasePhase,
])
const handlePurchase = useCallback(async () => {
setAnyTouched(true)
setPurchaseError(null)
const nextSendOnError =
sendTiming === 'later'
? validateSendOn(sendOn, sendTime, sendTimezone)
: null
setSendOnError(nextSendOnError)
if (!isFormValid || nextSendOnError) {
return
}
if (['google_pay', 'apple_pay'].includes(selectedMethod)) {
return
}
setPurchasing(true)
try {
if (stripeMethods.includes(selectedMethod)) {
if (!stripeDetails?.client_secret || stripeDetails?.error) {
setPurchaseError(
stripeDetails?.error ||
__(
'Unable to initialize Stripe payment.',
'webba-booking-lite'
)
)
return
}
await completeStripePurchase()
return
}
const response = await apiFetch<{
success?: boolean
message?: string
payment_required?: boolean
payment_method?: string
payment_url?: string
gift_card?: IGiftCardResult
}>({
path: '/wbk/v2/purchase-gift-card/',
method: 'POST',
data: buildPayload(),
})
if (!response?.success) {
setPurchaseError(
response?.message ||
__(
'Unable to start gift card purchase.',
'webba-booking-lite'
)
)
return
}
if (response.payment_required && response.payment_url) {
setPaymentUrl(response.payment_url)
setPurchasePhase('redirect_payment')
return
}
if (response.gift_card) {
setPurchaseResult(response.gift_card)
setPurchasePhase('success')
}
} catch (e: any) {
setPurchaseError(
e?.message ||
__(
'Unable to complete gift card purchase.',
'webba-booking-lite'
)
)
} finally {
setPurchasing(false)
}
}, [
setAnyTouched,
sendTiming,
validateSendOn,
sendOn,
sendTime,
sendTimezone,
setSendOnError,
isFormValid,
selectedMethod,
setPurchasing,
stripeDetails,
completeStripePurchase,
buildPayload,
setPurchaseError,
setPaymentUrl,
setPurchasePhase,
setPurchaseResult,
])
const handleWalletConfirm = useCallback(async () => {
setAnyTouched(true)
if (!isFormValid) {
return false
}
setPurchasing(true)
try {
return await completeStripePurchase()
} catch (e: any) {
setPurchaseError(
e?.message ||
__(
'Unable to complete gift card purchase.',
'webba-booking-lite'
)
)
return false
} finally {
setPurchasing(false)
}
}, [
setAnyTouched,
isFormValid,
setPurchasing,
completeStripePurchase,
setPurchaseError,
])
if (purchasePhase === 'cancelled') {
return
{__( 'Choose an amount and design, then add who it is for. After payment, you will receive a receipt and the recipient will get their gift card by email.', 'webba-booking-lite' )}