@godaddy/react
Version:
The `createCheckoutSession` function creates a new checkout session with GoDaddy's commerce API.
415 lines (413 loc) • 13.9 kB
JavaScript
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 };