UNPKG

@godaddy/react

Version:

The `createCheckoutSession` function creates a new checkout session with GoDaddy's commerce API.

415 lines (413 loc) 13.9 kB
import { $ as useDraftOrder, B as eventIds, K as TrackingEventType, M as Skeleton, Q as useIsPaymentDisabled, c as filterAndSortShippingMethods, et as useDraftOrderTotals, f as useStripePaymentIntent, o as useCheckoutContext, q as track, rt as useGoDaddyContext } from "./checkout-CCruxHvk.js"; import "./utils-DWBfAHfx.js"; import { t as useStripeCheckout } from "./use-stripe-checkout-pvP-pMaJ.js"; import { n as useGetShippingMethodByAddress, r as useGetPriceAdjustments, t as useGetTaxes } from "./use-get-taxes-mFqCMRYr.js"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { ExpressCheckoutElement, useElements } from "@stripe/react-stripe-js"; //#region src/components/checkout/payment/checkout-buttons/express/stripe.tsx function StripeExpressCheckoutForm() { const { t } = useGoDaddyContext(); const { session, setCheckoutErrors, isConfirmingCheckout } = useCheckoutContext(); const elements = useElements(); const isPaymentDisabled = useIsPaymentDisabled(); const { handleSubmit } = useStripeCheckout({ mode: "express" }); const isDisabled = isConfirmingCheckout || isPaymentDisabled; const { data: totals } = useDraftOrderTotals(); const { data: draftOrder } = useDraftOrder(); const getShippingMethodsByAddress = useGetShippingMethodByAddress(); const getTaxes = useGetTaxes(); const getPriceAdjustments = useGetPriceAdjustments(); const currencyCode = totals?.total?.currencyCode || "USD"; const [calculatedTaxes, setCalculatedTaxes] = useState(null); const [shippingMethods, setShippingMethods] = useState(null); const [selectedShippingRate, setSelectedShippingRate] = useState(null); const [shippingAddress, setShippingAddress] = useState(null); const [couponFetchStatus, setCouponFetchStatus] = useState("idle"); const appliedCouponCodeRef = useRef(null); const calculatedAdjustmentsRef = useRef(null); useEffect(() => { if (!draftOrder) return; if (couponFetchStatus === "fetching") return; const fetchPriceAdjustments = async () => { setCouponFetchStatus("fetching"); try { const allCodes = /* @__PURE__ */ new Set(); if (draftOrder?.discounts) { for (const discount of draftOrder.discounts) if (discount.code) allCodes.add(discount.code); } if (draftOrder?.lineItems) { for (const lineItem of draftOrder.lineItems) if (lineItem.discounts) { for (const discount of lineItem.discounts) if (discount.code) allCodes.add(discount.code); } } const discountCodes = Array.from(allCodes); if (discountCodes?.length && discountCodes?.[0]) { const result = await getPriceAdjustments.mutateAsync({ discountCodes: [discountCodes[0]] }); if (result) { appliedCouponCodeRef.current = discountCodes[0]; calculatedAdjustmentsRef.current = result; } } else { appliedCouponCodeRef.current = null; calculatedAdjustmentsRef.current = null; } } finally { setCouponFetchStatus("done"); } }; fetchPriceAdjustments(); }, [draftOrder, useMemo(() => { const allCodes = /* @__PURE__ */ new Set(); if (draftOrder?.discounts) { for (const discount of draftOrder.discounts) if (discount.code) allCodes.add(discount.code); } if (draftOrder?.lineItems) { for (const lineItem of draftOrder.lineItems) if (lineItem.discounts) { for (const discount of lineItem.discounts) if (discount.code) allCodes.add(discount.code); } } return Array.from(allCodes).sort().join(","); }, [draftOrder])]); const calculateExpressTaxes = useCallback(async ({ address, shippingAmount, discountAdjustments }) => { if (!address || !session?.enableTaxCollection) return null; const taxesRequest = { destination: { countryCode: address.country || "US", postalCode: address.postal_code || "", adminArea2: address.city || "", adminArea1: address.state || "" }, lines: [{ type: "SHIPPING", subtotalPrice: { currencyCode, value: shippingAmount } }], discountAdjustments: discountAdjustments || void 0 }; return await getTaxes.mutateAsync(taxesRequest); }, [ getTaxes, session?.enableTaxCollection, currencyCode ]); const getSortedShippingMethods = useCallback(async (address) => { const shippingMethodsData = await getShippingMethodsByAddress.mutateAsync({ countryCode: address.country || "US", postalCode: address.postal_code || "", adminArea2: address.city || "", adminArea1: address.state || "" }); setShippingMethods(shippingMethodsData || null); const orderSubTotal = totals?.subTotal?.value || 0; return filterAndSortShippingMethods({ shippingMethods: shippingMethodsData || [], orderSubTotal, experimentalRules: session?.experimental_rules }); }, [ getShippingMethodsByAddress, session?.experimental_rules, totals?.subTotal?.value ]); const convertToStripeShippingRates = useCallback((methods) => { return methods.map((method) => ({ id: method.displayName?.replace(/\s+/g, "-")?.toLowerCase() || "shipping", amount: method.cost?.value || 0, displayName: method.displayName || t.totals.shipping, deliveryEstimate: method.description || void 0 })); }, [t.totals.shipping]); const buildLineItems = useCallback(({ shippingAmount = 0, taxAmount = 0, discountAmount = 0 }) => { const items = []; const subtotal = totals?.subTotal?.value || 0; items.push({ name: t.totals.subtotal, amount: subtotal }); if (shippingAmount > 0) items.push({ name: t.totals.shipping, amount: shippingAmount }); if (taxAmount > 0) items.push({ name: t.totals.estimatedTaxes, amount: taxAmount }); if (discountAmount > 0) items.push({ name: t.totals.discount, amount: -discountAmount }); return items; }, [totals?.subTotal?.value, t.totals]); const recalculateAdjustments = useCallback(async ({ address, shippingAmount, shippingMethodName }) => { const currentCouponCode = appliedCouponCodeRef.current; if (!currentCouponCode) return null; try { const shippingLines = [{ subTotal: { currencyCode, value: shippingAmount }, name: shippingMethodName }]; const newAdjustments = await getPriceAdjustments.mutateAsync({ discountCodes: [currentCouponCode], shippingLines }); if (newAdjustments?.totalDiscountAmount) { calculatedAdjustmentsRef.current = newAdjustments; return newAdjustments; } calculatedAdjustmentsRef.current = null; appliedCouponCodeRef.current = null; return null; } catch { calculatedAdjustmentsRef.current = null; appliedCouponCodeRef.current = null; return null; } }, [currencyCode, getPriceAdjustments]); const handleReady = useCallback((event) => { if (event.availablePaymentMethods?.applePay) track({ eventId: eventIds.expressApplePayImpression, type: TrackingEventType.IMPRESSION, properties: { provider: "stripe" } }); }, []); const handleClick = useCallback((event) => { if (isDisabled) { event.reject(); return; } if (event.expressPaymentType === "apple_pay") track({ eventId: eventIds.expressApplePayClick, type: TrackingEventType.CLICK, properties: { paymentType: "apple_pay", provider: "stripe" } }); setCalculatedTaxes(null); setShippingMethods(null); setSelectedShippingRate(null); setShippingAddress(null); setCheckoutErrors(void 0); const discountAmount = calculatedAdjustmentsRef.current?.totalDiscountAmount?.value || 0; event.resolve({ lineItems: buildLineItems({ discountAmount }) }); }, [ buildLineItems, setCheckoutErrors, isDisabled ]); const handleShippingAddressChange = useCallback(async (event) => { const address = event.address; setShippingAddress(address); try { const methods = await getSortedShippingMethods(address); if (!methods || methods.length === 0) { event.reject(); return; } const stripeShippingRates = convertToStripeShippingRates(methods); const defaultRate = stripeShippingRates[0]; setSelectedShippingRate(defaultRate); const adjustments = await recalculateAdjustments({ address, shippingAmount: defaultRate.amount, shippingMethodName: defaultRate.displayName }); let taxAmount = 0; if (session?.enableTaxCollection) try { const taxesResult = await calculateExpressTaxes({ address, shippingAmount: defaultRate.amount, discountAdjustments: adjustments }); if (taxesResult?.totalTaxAmount?.value) { taxAmount = taxesResult.totalTaxAmount.value; setCalculatedTaxes(taxesResult); } } catch { setCheckoutErrors([t.apiErrors?.TAX_CALCULATION_ERROR || "Tax calculation error"]); } const discountAmount = adjustments?.totalDiscountAmount?.value || 0; const newTotal = (totals?.subTotal?.value || 0) + defaultRate.amount + taxAmount - discountAmount; elements?.update({ amount: newTotal }); event.resolve({ shippingRates: stripeShippingRates, lineItems: buildLineItems({ shippingAmount: defaultRate.amount, taxAmount, discountAmount }) }); } catch { event.reject(); } }, [ getSortedShippingMethods, convertToStripeShippingRates, recalculateAdjustments, calculateExpressTaxes, buildLineItems, session?.enableTaxCollection, setCheckoutErrors, elements, totals?.subTotal?.value, t.apiErrors ]); const handleShippingRateChange = useCallback(async (event) => { const selectedRate = event.shippingRate; setSelectedShippingRate(selectedRate); if (!shippingAddress) { event.reject(); return; } try { const adjustments = await recalculateAdjustments({ address: shippingAddress, shippingAmount: selectedRate.amount, shippingMethodName: selectedRate.displayName }); let taxAmount = 0; if (session?.enableTaxCollection) try { const taxesResult = await calculateExpressTaxes({ address: shippingAddress, shippingAmount: selectedRate.amount, discountAdjustments: adjustments }); if (taxesResult?.totalTaxAmount?.value) { taxAmount = taxesResult.totalTaxAmount.value; setCalculatedTaxes(taxesResult); } } catch { taxAmount = calculatedTaxes?.totalTaxAmount?.value || 0; } const discountAmount = adjustments?.totalDiscountAmount?.value || 0; const newTotal = (totals?.subTotal?.value || 0) + selectedRate.amount + taxAmount - discountAmount; elements?.update({ amount: newTotal }); event.resolve({ lineItems: buildLineItems({ shippingAmount: selectedRate.amount, taxAmount, discountAmount }) }); } catch { event.reject(); } }, [ shippingAddress, recalculateAdjustments, calculateExpressTaxes, buildLineItems, session?.enableTaxCollection, calculatedTaxes, elements, totals?.subTotal?.value ]); const handleConfirm = useCallback(async (event) => { try { const selectedShippingMethod = shippingMethods?.find((method) => method.displayName?.replace(/\s+/g, "-")?.toLowerCase() === selectedShippingRate?.id); await handleSubmit({ event, calculatedTaxes, calculatedAdjustments: calculatedAdjustmentsRef.current, shippingTotal: selectedShippingRate ? { currencyCode, value: selectedShippingRate.amount } : null, selectedShippingMethod: selectedShippingMethod || null }); track({ eventId: eventIds.expressApplePayCompleted, type: TrackingEventType.EVENT, properties: { paymentType: event.expressPaymentType, provider: "stripe" } }); } catch (error) { track({ eventId: eventIds.expressCheckoutError, type: TrackingEventType.EVENT, properties: { paymentType: event.expressPaymentType, provider: "stripe", errorType: error instanceof Error ? error.message : "unknown" } }); event.paymentFailed({ reason: "fail", message: t.errors.errorProcessingPayment }); } }, [ handleSubmit, t.errors.errorProcessingPayment, shippingMethods, selectedShippingRate, calculatedTaxes, currencyCode ]); const handleCancel = useCallback(() => { setCalculatedTaxes(null); setShippingMethods(null); setSelectedShippingRate(null); setShippingAddress(null); }, []); return /* @__PURE__ */ jsx("div", { className: isDisabled ? "opacity-50 pointer-events-none" : void 0, children: /* @__PURE__ */ jsx(ExpressCheckoutElement, { options: { paymentMethods: { applePay: "auto", googlePay: "auto", link: "never", paypal: "never", amazonPay: "never", klarna: "never" }, buttonHeight: 50, buttonType: { applePay: "plain", googlePay: "plain" }, buttonTheme: { applePay: "black", googlePay: "black" }, layout: { maxColumns: 2, maxRows: 1 }, shippingAddressRequired: !!session?.enableShippingAddressCollection, billingAddressRequired: !!session?.enableBillingAddressCollection, emailRequired: true, phoneNumberRequired: false, business: session?.storeName ? { name: session.storeName } : void 0 }, onReady: handleReady, onClick: handleClick, onShippingAddressChange: handleShippingAddressChange, onShippingRateChange: handleShippingRateChange, onConfirm: handleConfirm, onCancel: handleCancel }) }); } function StripeExpressCheckoutButton() { const { t } = useGoDaddyContext(); const { stripeConfig } = useCheckoutContext(); if (!stripeConfig) return /* @__PURE__ */ jsx("div", { className: "text-destructive", children: t.errors.stripeConfigMissing }); const { isLoading } = useStripePaymentIntent(); return /* @__PURE__ */ jsxs(Fragment, { children: [isLoading ? /* @__PURE__ */ jsx("div", { className: "grid gap-1 grid-cols-1", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full mb-1" }) }) : null, /* @__PURE__ */ jsx(StripeExpressCheckoutForm, {})] }); } //#endregion export { StripeExpressCheckoutButton, StripeExpressCheckoutForm };