import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { View, StyleSheet, TouchableOpacity, Platform, I18nManager, ScrollView, Keyboard, BackHandler, SafeAreaView } from 'react-native'; import { initStripe, useConfirmPayment } from '@stripe/stripe-react-native'; import NativeStripeSdk from '@stripe/stripe-react-native/src/NativeStripeSdk' import Picker from 'react-native-country-picker-modal'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons' import IconAntDesign from 'react-native-vector-icons/AntDesign'; import { useIsFocused } from '@react-navigation/native'; import ReactNativeHapticFeedback, { HapticFeedbackTypes } from "react-native-haptic-feedback"; import { Checkout as CheckoutController, useOrder, useSession, useApi, useLanguage, useUtils, useConfig, useToast, ToastType, useEvent } from 'ordering-components/native'; import { useTheme } from 'styled-components/native'; import { OText, OIcon, OModal, OButton } from '../shared'; import { AddressDetails } from '../AddressDetails'; import { PaymentOptions } from '../PaymentOptions'; import { DriverTips } from '../DriverTips'; import { NotFoundSource } from '../NotFoundSource'; import { UserDetails } from '../UserDetails'; import { PaymentOptionWallet } from '../PaymentOptionWallet'; import { PlaceSpot } from '../PlaceSpot' import { SignupForm } from '../SignupForm' import { LoginForm } from '../LoginForm' import { ChContainer, ChSection, ChHeader, ChAddress, CHMomentWrapper, ChPaymethods, ChDriverTips, ChErrors, ChBusinessDetails, ChUserDetails, ChCart, DeliveryOptionsContainer, DeliveryOptionItem, WalletPaymentOptionContainer, CartHeader, TopHeader, TopActions } from './styles'; import { Fade, Placeholder, PlaceholderLine } from 'rn-placeholder'; import { FloatingButton } from '../FloatingButton'; import { Container } from '../../layouts/Container'; import NavBar from '../NavBar'; import { OrderSummary } from '../OrderSummary'; import { getTypesText } from '../../utils'; import { CartStoresListing } from '../CartStoresListing'; import { PaymentOptionsWebView } from '../../../../../src/components/PaymentOptionsWebView'; import { DeviceOrientationMethods } from '../../../../../src/hooks/DeviceOrientation' const { useDeviceOrientation } = DeviceOrientationMethods const mapConfigs = { mapZoom: 16, mapSize: { width: 640, height: 190 } }; const driverTipsTypes = [1, 2] const manageErrorsToShow = (array = []) => { let stringError = '' const list = Array.isArray(array) ? array : Object.values(array) list.map((item: any, i: number) => { stringError += (i + 1) === array.length ? `- ${item?.message || item}` : `- ${item?.message || item}\n` }) return stringError; } const CheckoutUI = (props: any) => { const { navigation, cart, errors, placing, cartState, cartUuid, loyaltyPlansState, businessDetails, paymethodSelected, handlePaymethodChange, handlerClickPlaceOrder, onNavigationRedirect, deliveryOptionSelected, instructionsOptions, handleChangeDeliveryOption, currency, merchantId, setPlaceSpotNumber, maxDate, androidAppId, urlscheme, checkoutFieldsState } = props const theme = useTheme(); const isFocused = useIsFocused(); const styles = StyleSheet.create({ btnBackArrow: { borderWidth: 0, backgroundColor: theme.colors.white, borderColor: theme.colors.white, shadowColor: theme.colors.white, display: 'flex', justifyContent: 'flex-start', paddingLeft: 0, }, paddSection: { padding: 20 }, pagePadding: { paddingHorizontal: 20 }, icon: { top: 15, right: Platform.OS === 'ios' ? 5 : (I18nManager.isRTL ? 30 : 0), position: 'absolute', fontSize: 20 }, detailWrapper: { paddingHorizontal: 20, width: '100%' }, wrapperNavbar: { paddingVertical: 2, paddingHorizontal: 20, backgroundColor: theme?.colors?.white, borderWidth: 0 } }) const [, { showToast }] = useToast(); const [, t] = useLanguage(); const [{ user, token, loading: userLoading }, { login }] = useSession(); const [ordering] = useApi() const [{ configs }] = useConfig(); const [{ parsePrice, parseDate }] = useUtils(); const [{ options, carts, loading }, { confirmCart }] = useOrder(); const [events] = useEvent() const [orientationState] = useDeviceOrientation(); const [isReadMore, setIsReadMore] = useState(false) const [lengthMore, setLengthMore] = useState(false) const WIDTH_SCREEN = orientationState?.dimensions?.width const [errorCash, setErrorCash] = useState(false); const [userErrors, setUserErrors] = useState([]); const [isUserDetailsEdit, setIsUserDetailsEdit] = useState(false); const [phoneUpdate, setPhoneUpdate] = useState(false); const [openChangeStore, setOpenChangeStore] = useState(false) const [isDeliveryOptionModalVisible, setIsDeliveryOptionModalVisible] = useState(false) const [showGateway, setShowGateway] = useState({ closedByUser: false, open: false }); const [webviewPaymethod, setWebviewPaymethod] = useState(null) const [isOpen, setIsOpen] = useState(false) const [requiredFields, setRequiredFields] = useState([]) const [orderTypeValidationFields, setOrderTypeValidationFields] = useState([]) const [openModal, setOpenModal] = useState({ login: false, signup: false, isGuest: false }) const [allowedGuest, setAllowedGuest] = useState(false) const [placeByMethodPay, setPlaceByMethodPay] = useState(false) const [methodPaySupported, setMethodPaySupported] = useState({ enabled: false, message: null, loading: true }) const [paymethodClicked, setPaymethodClicked] = useState(null) const [showTitle, setShowTitle] = useState(false) const [cardList, setCardList] = useState({ cards: [], loading: false, error: null }) const [isGiftCardCart, setIsGiftCardCart] = useState(!cart?.business_id) const [userHasCards, setUserHasCards] = useState(false) const containerRef = useRef() const cardsMethods = ['credomatic'] const stripePaymethods: any = ['stripe', 'stripe_connect', 'stripe_redirect'] const cardsPaymethods: any = ['stripe', 'stripe_connect'] const notFields = ['coupon', 'driver_tip', 'mobile_phone', 'address', 'zipcode', 'address_notes', 'comments'] const checkoutFields = useMemo(() => checkoutFieldsState?.fields?.filter((field: any) => field.order_type_id === options?.type), [checkoutFieldsState, options]) const guestCheckoutDriveTip = useMemo(() => checkoutFields?.find((field: any) => driverTipsTypes.includes(field.order_type_id) && field?.validation_field?.code === 'driver_tip'), [JSON.stringify(checkoutFields), options]) const guestCheckoutComment = useMemo(() => checkoutFields?.find((field: any) => field.order_type_id === options?.type && field?.validation_field?.code === 'comments'), [JSON.stringify(checkoutFields), options]) const guestCheckoutCoupon = useMemo(() => checkoutFields?.find((field: any) => field.order_type_id === options?.type && field?.validation_field?.code === 'coupon'), [JSON.stringify(checkoutFields), options]) const guestCheckoutZipcode = useMemo(() => checkoutFields?.find((field: any) => field.order_type_id === options?.type && field?.validation_field?.code === 'zipcode'), [JSON.stringify(checkoutFields), options]) const placeSpotTypes = [3, 4, 5] const placeSpotsEnabled = placeSpotTypes.includes(options?.type) const businessConfigs = businessDetails?.business?.configs ?? [] const isWalletCashEnabled = businessConfigs.find((config: any) => config.key === 'wallet_cash_enabled')?.value === '1' const isWalletCreditPointsEnabled = businessConfigs.find((config: any) => config.key === 'wallet_credit_point_enabled')?.value === '1' const isWalletEnabled = configs?.cash_wallet?.value && configs?.wallet_enabled?.value === '1' && (isWalletCashEnabled || isWalletCreditPointsEnabled) const isBusinessChangeEnabled = configs?.cart_change_business_validation?.value === '1' const isChewLayout = theme?.header?.components?.layout?.type?.toLowerCase() === 'chew' const hideBusinessAddress = theme?.checkout?.components?.business?.components?.address?.hidden const hideBusinessDetails = theme?.checkout?.components?.business?.hidden const hideBusinessMap = theme?.checkout?.components?.business?.components?.map?.hidden const hideCustomerDetails = theme?.checkout?.components?.customer?.hidden const isGuestCheckoutEnabled = configs?.guest_checkout_enabled?.value === '1' const creditPointPlan = loyaltyPlansState?.result?.find((loyal: any) => loyal.type === 'credit_point') const creditPointPlanOnBusiness = creditPointPlan?.businesses?.find((b: any) => b.business_id === cart?.business_id && b.accumulates) const methodsPay = ['google_pay', 'apple_pay'] const commentDelayTime = 1500 const isPreOrder = configs?.preorder_status_enabled?.value === '1' const subtotalWithTaxes = cart?.taxes?.reduce((acc: any, item: any) => { if (item?.type === 1) return acc = acc + item?.summary?.tax return acc = acc }, cart?.subtotal) const allowDriverTipPickup = configs?.driver_tip_allowed_at_pickup?.value === '1' && options?.type === 2 const validateCommentsCartField = (guestCheckoutComment?.enabled && (user?.guest_id ? guestCheckoutComment?.required_with_guest : guestCheckoutComment?.required)) && (cart?.comment === null || cart?.comment?.trim().length === 0) const validateDriverTipField = (options.type === 1 || allowDriverTipPickup) && (guestCheckoutDriveTip?.enabled && (user?.guest_id ? guestCheckoutDriveTip?.required_with_guest : guestCheckoutDriveTip?.required)) && (Number(cart?.driver_tip) <= 0) const validateCouponField = (guestCheckoutCoupon?.enabled && (user?.guest_id ? guestCheckoutCoupon?.required_with_guest : guestCheckoutCoupon?.required)) && !cart?.offers?.some((offer: any) => offer?.type === 2) const validateZipcodeCard = (guestCheckoutZipcode?.enabled && (user?.guest_id ? guestCheckoutZipcode?.required_with_guest : guestCheckoutZipcode?.required)) && paymethodSelected?.gateway === 'stripe' && paymethodSelected?.data?.card && !paymethodSelected?.data?.card?.zipcode const isDisabledButtonPlace = loading || !cart?.valid || (!paymethodSelected && cart?.balance > 0) || placing || errorCash || subtotalWithTaxes < cart?.minimum || (cardsMethods.includes(paymethodSelected?.gateway) && cardList?.cards?.length === 0) || (validateCommentsCartField) || (validateDriverTipField && !isGiftCardCart) || (validateZipcodeCard) || (methodsPay.includes(paymethodSelected?.gateway) && (!methodPaySupported.enabled || methodPaySupported.loading)) || validateCommentsCartField || validateDriverTipField || validateCouponField || validateZipcodeCard || (!userHasCards && cardsPaymethods.includes(paymethodSelected?.gateway)) || (!isGuestCheckoutEnabled && !!user?.guest_id) const driverTipsOptions = typeof configs?.driver_tip_options?.value === 'string' ? JSON.parse(configs?.driver_tip_options?.value) || [] : configs?.driver_tip_options?.value || [] const driverTipsField = !cartState.loading && cart && cart?.business_id && (options.type === 1 || allowDriverTipPickup) && cart?.status !== 2 && (guestCheckoutDriveTip?.enabled) && driverTipsOptions.length > 0 const configTypes = configs?.order_types_allowed?.value.split('|').map((value: any) => Number(value)) || [] const cartsWithProducts = carts && Object.values(carts).filter((cart: any) => cart.products.length) || null const isSandboxCredomatic = configs?.credomatic_integration_sandbox?.value === '1' const credomaticKeyId = isSandboxCredomatic ? configs?.credomatic_integration_public_sandbox_key?.value : configs?.credomatic_integration_public_production_key?.value const credomaticUrl = `https://integrations.ordering.co/credomatic/front/auth_mobile.html?title=${t('CREDOMATIC_PAYMENT', 'Credomatic payment')}&body=${t('CREDOMATIC_PROCESSING', 'Processing transaction')}` const deliveryOptions = instructionsOptions?.result && instructionsOptions?.result?.filter((option: any) => option?.enabled)?.map((option: any) => { return { value: option?.id, key: option?.id, label: t(option?.name.toUpperCase().replace(/\s/g, '_'), option?.name) } }) const handleMomentClick = () => { if (isPreOrder) { navigation.navigate('MomentOption', { business: businessDetails?.business }) } } const vibrateApp = (impact?: HapticFeedbackTypes) => { const options = { enableVibrateFallback: true, ignoreAndroidSystemSettings: false }; ReactNativeHapticFeedback.trigger(impact || "impactLight", options); } const handleSuccessSignup = async (user: any) => { await login({ user, token: user?.session?.access_token }) setOpenModal({ ...openModal, signup: false, isGuest: false }) } const handleSuccessLogin = (user: any) => { if (user) setOpenModal({ ...openModal, login: false }) } const handleOpenGuestSignup = () => { setOpenModal({ ...openModal, signup: true, isGuest: true }) } const handlePlaceOrder = (confirmPayment: any, forcePlace: boolean = false) => { if (stripePaymethods.includes(paymethodSelected?.gateway) && user?.guest_id) { setOpenModal({ ...openModal, signup: true, isGuest: true }) return } if (!userErrors.length && (!requiredFields?.length) || forcePlace) { vibrateApp() handlerClickPlaceOrder && handlerClickPlaceOrder(null, { isNative: true }, confirmPayment, NativeStripeSdk?.dismissPlatformPay) return } if (requiredFields?.length) { setIsOpen(true) return } let stringError = '' Object.values(userErrors).map((item: any, i: number) => { stringError += (i + 1) === userErrors.length ? `- ${item?.message || item}` : `- ${item?.message || item}\n` }) showToast(ToastType.Error, stringError) setIsUserDetailsEdit(true) } const handlePlaceOrderAsGuest = () => { setIsOpen(false) handlerClickPlaceOrder && handlerClickPlaceOrder() } const handlePaymentMethodClick = (paymethod: any) => { setShowGateway({ closedByUser: false, open: true }) setWebviewPaymethod(paymethod) } const onFailPaypal = async () => { if (showGateway.closedByUser === true) { await confirmCart(cart.uuid) } } const changeDeliveryOption = (option: any) => { handleChangeDeliveryOption(option) setIsDeliveryOptionModalVisible(false) } const checkValidationFields = () => { setUserErrors([]) const errors: Array = [] const userSelected = user const _requiredFields: Array = [] Object.values(checkoutFieldsState?.fields).map((field: any) => { if (options?.type === field?.order_type_id && field?.enabled && field?.required && !notFields.includes(field?.validation_field?.code) ) { if (userSelected && !userSelected[field?.validation_field?.code]) { _requiredFields.push(field?.validation_field?.code) } } }) const mobilePhoneField: any = Object.values(checkoutFieldsState?.fields)?.find((field: any) => field?.order_type_id === options?.type && field?.validation_field?.code === 'mobile_phone') if ( userSelected && !userSelected?.cellphone && ((mobilePhoneField?.enabled && mobilePhoneField?.required) || configs?.verification_phone_required?.value === '1') ) { _requiredFields.push('cellphone') } setRequiredFields(_requiredFields) setUserErrors(errors) } const checkGuestValidationFields = () => { const userSelected = user const _requiredFields = checkoutFieldsState?.fields .filter((field: any) => (field?.order_type_id === options?.type) && field?.enabled && field?.required_with_guest && !notFields.includes(field?.validation_field?.code) && field?.validation_field?.code !== 'email' && userSelected && !userSelected[field?.validation_field?.code]) const requiredFieldsCode = _requiredFields.map((item: any) => item?.validation_field?.code) const guestCheckoutCellPhone = checkoutFieldsState?.fields?.find((field: any) => field.order_type_id === options?.type && field?.validation_field?.code === 'mobile_phone') const guestCheckoutEmail = checkoutFieldsState?.fields?.find((field: any) => field.order_type_id === options?.type && field?.validation_field?.code === 'email') if ( userSelected && !userSelected?.guest_cellphone && ((guestCheckoutCellPhone?.enabled && guestCheckoutCellPhone?.required_with_guest) || configs?.verification_phone_required?.value === '1') ) { requiredFieldsCode.push('cellphone') } if ( userSelected && !userSelected?.guest_email && guestCheckoutEmail?.enabled && guestCheckoutEmail?.required_with_guest ) { requiredFieldsCode.push('email') } setRequiredFields(requiredFieldsCode) } const togglePhoneUpdate = (val: boolean) => { setPhoneUpdate(val) } const handleScroll = ({ nativeEvent: { contentOffset } }: any) => { setShowTitle(contentOffset.y > 30) } const handleRedirect = () => { props.fromProductsList ? navigation?.goBack() : onNavigationRedirect('BottomTab', { screen: 'Cart' }, !props.fromMulti) } useEffect(() => { if (checkoutFieldsState?.loading || userLoading) return if (user?.guest_id) { checkGuestValidationFields() } else { checkValidationFields() } }, [checkoutFieldsState, user, options?.type]) useEffect(() => { if (errors) { const errorText = manageErrorsToShow(errors) showToast(ToastType.Error, errorText) } }, [errors]) useEffect(() => { if (cart?.products?.length === 0 || !userLoading) return if (cart?.business_id !== null) { onNavigationRedirect('Business', { store: cart?.business?.slug, header: null, logo: null, fromMulti: props.fromMulti }) return } if (isGiftCardCart) { onNavigationRedirect('Wallets') return } }, [cart?.products?.length]) useEffect(() => { if (cart?.products?.length > 0) { if (cart?.uuid && cart?.business_id === null) { setIsGiftCardCart(true) } else { setIsGiftCardCart(false) } } }, [cart?.uuid, cart?.products?.length]) useEffect(() => { onFailPaypal() }, [showGateway.closedByUser]) const HeaderTitle = (props: any) => { const { text } = props return ( {text} ) } useEffect(() => { cart && events.emit('checkout_started', cart) }, []) useEffect(() => { if (cart?.paymethod_data?.gateway === 'credomatic') { if (cart?.paymethod_data?.status === 2) { setShowGateway({ ...showGateway, open: true }) } else if (cart?.paymethod_data?.gateway === 'credomatic' && cart?.paymethod_data?.status === 4) { setShowGateway({ ...showGateway, open: false }) } } }, [cart?.paymethod_data]) const onTextLayout = useCallback((e: any) => { setLengthMore((e.nativeEvent.lines.length == 3 && e.nativeEvent.lines[2].width > WIDTH_SCREEN * .76) || e.nativeEvent.lines.length > 3) }, []) useEffect(() => { if (!isFocused) return if (!cartState?.loading && (cartState?.error || typeof cartState?.cart === 'string')) { const error = cartState?.error || typeof cartState.cart === 'string' && cartState.cart if (error) { showToast(ToastType.Error, cartState?.error || cartState.cart) navigation.navigate('BusinessList') } } }, [cartState?.error, cartState?.cart, cartState?.loading, isFocused]) useEffect(() => { const onBackFunction = () => { if (webviewPaymethod?.gateway === 'paypal' && showGateway.open) { setShowGateway({ open: false, closedByUser: true }) return true } else { return false } } BackHandler.addEventListener('hardwareBackPress', onBackFunction) return () => { BackHandler.removeEventListener('hardwareBackPress', onBackFunction) } }, [BackHandler, webviewPaymethod?.gateway, showGateway.open]) return ( <> handleRedirect()}> {showTitle && ( {t('CHECKOUT', 'Checkout')} )} handleRedirect()} showCall={false} btnStyle={{ paddingLeft: 0, paddingTop: Platform.OS == 'ios' ? 0 : 2 }} titleWrapStyle={{ paddingHorizontal: 0 }} titleStyle={{ marginRight: 0, marginLeft: 0 }} style={{ marginTop: 20 }} /> {!isGiftCardCart && ( navigation.navigate('OrderTypes', { configTypes: configTypes })}> {t(getTypesText(options?.type || 1), 'Delivery')} handleMomentClick()} disabled={loading} > {options?.moment ? parseDate(options?.moment, { outputFormat: configs?.dates_moment_format?.value }) : t('ASAP_ABBREVIATION', 'ASAP')} {isPreOrder && ( )} )} {!isGiftCardCart && !hideBusinessDetails && ( { (businessDetails?.loading || cartState.loading || !businessDetails?.business || Object.values(businessDetails?.business).length === 0) && !businessDetails?.error && ( )} { !cartState.loading && businessDetails?.business && Object.values(businessDetails?.business).length > 0 && ( <> {businessDetails?.business?.name} {businessDetails?.business?.email} {businessDetails?.business?.cellphone} {!hideBusinessAddress && ( {businessDetails?.business?.address} )} {businessDetails?.business?.address_notes && ( <> {businessDetails?.business?.address_notes} {lengthMore && ( setIsReadMore(!isReadMore)} > {isReadMore ? t('SHOW_LESS', 'Show less') : t('READ_MORE', 'Read more')} )} )} )} {businessDetails?.error && businessDetails?.error?.length > 0 && ( )} )} {!hideCustomerDetails && ( {cartState.loading ? ( ) : ( (user?.guest_id && !allowedGuest) ? ( setOpenModal({ ...openModal, signup: true })} /> setOpenModal({ ...openModal, login: true })} /> {isGuestCheckoutEnabled && ( setAllowedGuest(true)} /> )} ) : ( } cartStatus={cart?.status} businessId={cart?.business_id} useValidationFields useDefualtSessionManager useSessionUser isCheckout phoneUpdate={phoneUpdate} togglePhoneUpdate={togglePhoneUpdate} isOrderTypeValidationField requiredFields={requiredFields} checkoutFields={checkoutFields} /> ) )} )} {options?.type === 1 && !isGiftCardCart && ( {cartState.loading || deliveryOptionSelected === undefined ? ( ) : ( setIsDeliveryOptionModalVisible(false)} withCountryNameButton renderFlagButton={() => ( setIsDeliveryOptionModalVisible(true)}> {deliveryOptions.find((option: any) => option.value === deliveryOptionSelected)?.label} )} flatListProps={{ keyExtractor: (item: any) => item.value, data: deliveryOptions || [], renderItem: ({ item }: any) => ( !!cart?.uuid && changeDeliveryOption(item.value)} disabled={ deliveryOptionSelected === item.value } > {item.label} ) }} /> )} )} {!cartState.loading && (cart?.status === 2 || cart?.status === 4) && ( {!cartState.loading && cart?.status === 2 && ( {t('CART_STATUS_PENDING_MESSAGE_APP', 'Your order is being processed, please wait a little more. if you\'ve been waiting too long, please reload the app')} )} )} {!isGiftCardCart && !hideBusinessMap && ( {(businessDetails?.loading || cartState.loading) ? ( ) : ( } /> )} )} {driverTipsField && ( )} {!cartState.loading && cart && cart?.status !== 2 && cart?.valid && ( {!cartState.loading && cart?.status === 4 && ( {t('CART_STATUS_CANCEL_MESSAGE', 'The payment has not been successful, please try again')} )} )} {!cartState.loading && cart && isWalletEnabled && businessDetails?.business?.configs && ( )} {!cartState.loading && placeSpotsEnabled && !isGiftCardCart && ( <> )} {!cartState.loading && cart && ( {cartsWithProducts?.length > 0 && cart?.products?.length === 0 ? ( ) : ( <> {!isGiftCardCart && ( onNavigationRedirect('Business', { store: cart?.business?.slug })} > {t('ADD_PRODUCTS', 'Add products')} )} {isBusinessChangeEnabled && ( setOpenChangeStore(true)} style={{ alignSelf: 'flex-start' }} > {t('CHANGE_STORE', 'Change store')} )} )} )} {!cartState.loading && cart && ( {!cart?.valid_address && cart?.status !== 2 && ( {t('INVALID_CART_ADDRESS', 'Selected address is invalid, please select a closer address.')} )} {(!paymethodSelected && cart?.balance > 0) && cart?.status !== 2 && cart?.valid && ( {t('WARNING_NOT_PAYMENT_SELECTED', 'Please, select a payment method to place order.')} )} {!cart?.valid_products && cart?.status !== 2 && cart?.total !== 0 && ( <> {t('WARNING_INVALID_PRODUCTS_CHECKOUT', 'To continue with your checkout, please remove from your cart the products that are not available.')} )} {cart?.valid_preorder !== undefined && !cart?.valid_preorder && ( {t('INVALID_CART_MOMENT', 'Selected schedule time is invalid, please select a schedule into the business schedule interval.')} )} {validateDriverTipField && !isGiftCardCart && ( {t('WARNING_INVALID_DRIVER_TIP', 'Driver Tip is required.')} )} {validateCommentsCartField && ( {t('WARNING_INVALID_CART_COMMENTS', 'Cart comments is required.')} )} {validateZipcodeCard && ( {t('WARNING_CARD_ZIPCODE_REQUIRED', 'Your card selected has not zipcode')} )} {validateCouponField && ( {t('WARNING_INVALID_COUPON_FIELD', 'Coupon is required.')} )} {(!isGuestCheckoutEnabled && !!user?.guest_id) && ( {t('LOGIN_SIGN_UP_COMPLETE_ORDER', 'Login/Sign up to complete your order.')} )} )} setOpenChangeStore(false)} > setOpenChangeStore(false)} /> setIsOpen(false)} showToastInsideModal > { setIsOpen(false) if (paymethodClicked) { setPaymethodClicked({ ...paymethodClicked, confirmed: true }) } else { handlePlaceOrder(null, true) } }} setIsOpen={setIsOpen} /> setOpenModal({ ...openModal, signup: false, isGuest: false })} > setOpenModal({ ...openModal, login: false })} > {!cartState.loading && cart && cart?.status !== 2 && ( vibrateApp() : methodsPay.includes(paymethodSelected?.gateway) ? () => setPlaceByMethodPay(true) : () => handlePlaceOrder(null)} isSecondaryBtn={isDisabledButtonPlace} disabled={isDisabledButtonPlace} btnText={subtotalWithTaxes >= cart?.minimum ? ( placing ? t('PLACING', 'Placing') : loading ? t('LOADING', 'Loading') : t('PLACE_ORDER', 'Place Order') ) : (`${t('MINIMUN_SUBTOTAL_ORDER', 'Minimum subtotal order:')} ${parsePrice(cart?.minimum)}`) } btnRightValueShow btnRightValue={parsePrice(cart?.balance)} iosBottom={30} /> )} {webviewPaymethod?.gateway === 'paypal' && showGateway.open && ( )} {webviewPaymethod?.gateway === 'square' && showGateway.open && ( )} {cart?.paymethod_data?.gateway === 'credomatic' && cart?.paymethod_data?.status === 2 && showGateway.open && ( )} ) } export const Checkout = (props: any) => { const { errors, clearErrors, cartUuid, stripePaymentOptions, onNavigationRedirect, } = props const [, { showToast }] = useToast(); const [, t] = useLanguage(); const [{ token }] = useSession(); const [ordering] = useApi(); const [, { confirmCart }] = useOrder(); const { confirmPayment, loading: confirmPaymentLoading } = useConfirmPayment(); const [cartState, setCartState] = useState({ loading: true, error: [], cart: null }); const [orderState] = useOrder() const getOrder = async (cartId: any) => { try { let result: any = {} const cartsWithProducts = orderState?.carts && (Object.values(orderState?.carts)?.filter(cart => cart?.products && cart?.products?.length) || null) const cart = cartsWithProducts?.find((cart: any) => cart.uuid === cartId) if (cart) { result = { ...cart } } else { setCartState({ ...cartState, loading: true }) const url = `${ordering.root}/carts/${cartId}` const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` } }) const content = await response.json(); result = content.result } let publicKey = null try { const { content } = await ordering.setAccessToken(token).paymentCards().getCredentials(); if (!content.error) { publicKey = content.result.publishable; } } catch (error) { publicKey = null } if (result.status === 1 && result.order?.uuid) { onNavigationRedirect('OrderDetails', { orderId: result.order.uuid }) setCartState({ ...cartState, loading: false }) } else if (result.status === 2 && result.paymethod_data?.gateway === 'stripe_redirect') { try { const confirmCartRes = await confirmCart(cartUuid) if (confirmCartRes.error) { showToast(ToastType.Error, confirmCartRes.error.message) } if (confirmCartRes.result.order?.uuid) { onNavigationRedirect('OrderDetails', { orderId: confirmCartRes.result.order.uuid, isFromCheckout: true }) } setCartState({ ...cartState, loading: false, cart: result }) } catch (err: any) { showToast(ToastType.Error, err?.toString() || err.message) } } else if (result.status === 2 && stripePaymentOptions.includes(result.paymethod_data?.gateway)) { const clientSecret = result.paymethod_data?.result?.client_secret const paymentMethodId = result.paymethod_data?.data?.source_id; initStripe({ publishableKey: publicKey }); try { const { paymentIntent, error } = await confirmPayment(clientSecret, { type: 'Card', paymentMethodId }); try { const confirmCartRes = await confirmCart(cartUuid) if (confirmCartRes.error) { showToast(ToastType.Error, confirmCartRes.error.message) setCartState({ ...cartState, loading: false, cart: result }) return } if (confirmCartRes.result.order?.uuid) { onNavigationRedirect('OrderDetails', { orderId: confirmCartRes.result.order.uuid, isFromCheckout: true }) setCartState({ ...cartState, loading: false }) } else { showToast(ToastType.Error, t('FAILED_PAYMENT', 'The payment has failed')); const cart = Array.isArray(result) ? null : result setCartState({ ...cartState, loading: false, cart, error: cart ? null : result }) return } } catch (error: any) { showToast(ToastType.Error, error?.toString() || error.message) } } catch (error) { showToast(ToastType.Error, t('FAILED_PAYMENT', 'The payment has failed')); const cart = Array.isArray(result) ? null : result setCartState({ ...cartState, loading: false, cart, error: cart ? null : result }) } } else { const cart = Array.isArray(result) ? null : result setCartState({ ...cartState, loading: false, cart, error: cart ? null : result }) } } catch (e: any) { setCartState({ ...cartState, loading: false, error: [e.toString()] }) } } useEffect(() => { if (errors) { const errorText = manageErrorsToShow(errors) showToast(ToastType.Error, errorText) clearErrors && clearErrors() } }, [errors]) useEffect(() => { if (token && cartUuid) { getOrder(cartUuid) } }, [token, cartUuid]) const checkoutProps = { ...props, UIComponent: CheckoutUI, cartState, uuid: cartUuid } return ( <> ) }