-
+
) : null}
diff --git a/packages/react/src/components/checkout/payment/utils/stripe-provider.tsx b/packages/react/src/components/checkout/payment/utils/stripe-provider.tsx
index 53771437..81f58f6d 100644
--- a/packages/react/src/components/checkout/payment/utils/stripe-provider.tsx
+++ b/packages/react/src/components/checkout/payment/utils/stripe-provider.tsx
@@ -42,6 +42,8 @@ export function StripeProvider({ children }: { children: React.ReactNode }) {
amount: amount,
currency,
capture_method: 'manual',
+ paymentMethodCreation: 'manual',
+ payment_method_types: ['card'],
}}
>
diff --git a/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts b/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts
index bcaa1e3b..3cd0c5f4 100644
--- a/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts
+++ b/packages/react/src/components/checkout/payment/utils/use-build-payment-request.ts
@@ -1,5 +1,7 @@
-import type { CreateTokenCardData } from '@stripe/stripe-js';
-import type { ConfirmationTokenCreateParams } from '@stripe/stripe-js/dist/api/confirmation-tokens';
+import type {
+ CreateTokenCardData,
+ PaymentMethodCreateParams,
+} from '@stripe/stripe-js';
import { useMemo } from 'react';
import { useCheckoutContext } from '@/components/checkout/checkout';
import {
@@ -170,7 +172,7 @@ export function useBuildPaymentRequest(): {
googlePayRequest: GooglePayRequest;
payPalRequest: PayPalRequest;
stripePaymentCardRequest: CreateTokenCardData;
- stripePaymentExpressRequest: ConfirmationTokenCreateParams;
+ stripePaymentExpressRequest: PaymentMethodCreateParams;
poyntCardRequest: PoyntCardRequest;
poyntExpressRequest: PoyntExpressRequest;
poyntStandardRequest: PoyntStandardRequest;
@@ -508,32 +510,19 @@ export function useBuildPaymentRequest(): {
address_country: order?.billing?.address?.countryCode || undefined,
};
- const stripePaymentExpressRequest: ConfirmationTokenCreateParams = {
- payment_method_data: {
- billing_details: {
- name:
- `${order?.billing?.firstName || ''} ${order?.billing?.lastName || ''}`.trim() ||
- undefined,
- email: order?.billing?.email || undefined,
- address: {
- line1: order?.billing?.address?.addressLine1 || undefined,
- line2: order?.billing?.address?.addressLine2 || undefined,
- city: order?.billing?.address?.adminArea2 || undefined,
- state: order?.billing?.address?.adminArea1 || undefined,
- postal_code: order?.billing?.address?.postalCode || undefined,
- country: order?.billing?.address?.countryCode || undefined,
- },
- },
- },
- shipping: {
- name: `${order?.shipping?.firstName || ''} ${order?.shipping?.lastName || ''}`.trim(),
+ const stripePaymentExpressRequest: PaymentMethodCreateParams = {
+ billing_details: {
+ name:
+ `${order?.billing?.firstName || ''} ${order?.billing?.lastName || ''}`.trim() ||
+ undefined,
+ email: order?.billing?.email || undefined,
address: {
- line1: order?.shipping?.address?.addressLine1 || null,
- line2: order?.shipping?.address?.addressLine2 || null,
- city: order?.shipping?.address?.adminArea2 || null,
- state: order?.shipping?.address?.adminArea1 || null,
- postal_code: order?.shipping?.address?.postalCode || null,
- country: order?.shipping?.address?.countryCode || null,
+ line1: order?.billing?.address?.addressLine1 || undefined,
+ line2: order?.billing?.address?.addressLine2 || undefined,
+ city: order?.billing?.address?.adminArea2 || undefined,
+ state: order?.billing?.address?.adminArea1 || undefined,
+ postal_code: order?.billing?.address?.postalCode || undefined,
+ country: order?.billing?.address?.countryCode || undefined,
},
},
};
diff --git a/packages/react/src/components/checkout/payment/utils/use-stripe-checkout.ts b/packages/react/src/components/checkout/payment/utils/use-stripe-checkout.ts
index 982f6366..4ece4284 100644
--- a/packages/react/src/components/checkout/payment/utils/use-stripe-checkout.ts
+++ b/packages/react/src/components/checkout/payment/utils/use-stripe-checkout.ts
@@ -1,4 +1,5 @@
import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js';
+import type { StripeExpressCheckoutElementConfirmEvent } from '@stripe/stripe-js';
import { useCallback, useState } from 'react';
import { useCheckoutContext } from '@/components/checkout/checkout';
import { useBuildPaymentRequest } from '@/components/checkout/payment/utils/use-build-payment-request';
@@ -7,6 +8,11 @@ import {
useConfirmCheckout,
} from '@/components/checkout/payment/utils/use-confirm-checkout';
import { GraphQLErrorWithCodes } from '@/lib/graphql-with-errors';
+import type {
+ CalculatedAdjustments,
+ CalculatedTaxes,
+ ShippingMethod,
+} from '@/types';
import { PaymentMethodType } from '@/types';
type UseStripeCheckoutOptions = {
@@ -14,6 +20,21 @@ type UseStripeCheckoutOptions = {
clientSecret?: string | null;
};
+// Express checkout data to pass to confirmCheckout
+export type StripeExpressCheckoutData = {
+ // Stripe confirm event data
+ event: StripeExpressCheckoutElementConfirmEvent;
+ // Calculated values from express checkout flow
+ calculatedTaxes?: CalculatedTaxes | null;
+ calculatedAdjustments?: CalculatedAdjustments | null;
+ // Shipping info
+ shippingTotal?: {
+ currencyCode: string;
+ value: number;
+ } | null;
+ selectedShippingMethod?: ShippingMethod | null;
+};
+
export function useStripeCheckout({ mode }: UseStripeCheckoutOptions) {
const stripe = useStripe();
const elements = useElements();
@@ -22,94 +43,188 @@ export function useStripeCheckout({ mode }: UseStripeCheckoutOptions) {
const { stripePaymentExpressRequest } = useBuildPaymentRequest();
const [isProcessingPayment, setIsProcessingPayment] = useState(false);
- const handleSubmit = useCallback(async () => {
- setIsProcessingPayment(true);
- try {
- if (!stripe || !elements) {
- return;
- }
-
- if (mode === 'card') {
- const cardElement = elements.getElement(CardElement);
-
- if (!cardElement) {
+ const handleSubmit = useCallback(
+ async (expressData?: StripeExpressCheckoutData) => {
+ setIsProcessingPayment(true);
+ try {
+ if (!stripe || !elements) {
return;
}
- const { paymentMethod, error } = await stripe.createPaymentMethod({
- card: cardElement,
- type: 'card',
- });
+ if (mode === 'card') {
+ const cardElement = elements.getElement(CardElement);
- if (error) {
- setCheckoutErrors([error.code || 'payment_method_creation_failed']);
- return;
- }
+ if (!cardElement) {
+ return;
+ }
+
+ const { paymentMethod, error } = await stripe.createPaymentMethod({
+ card: cardElement,
+ type: 'card',
+ });
+
+ if (error) {
+ setCheckoutErrors([error.code || 'TRANSACTION_PROCESSING_FAILED']);
+ return;
+ }
- if (paymentMethod) {
- try {
- await confirmCheckout.mutateAsync({
- paymentToken: paymentMethod.id,
- paymentType: PaymentMethodType.CREDIT_CARD,
- paymentProvider: PaymentProvider.STRIPE,
- });
- } catch (err: unknown) {
- if (err instanceof GraphQLErrorWithCodes) {
- setCheckoutErrors(err.codes);
+ if (paymentMethod) {
+ try {
+ await confirmCheckout.mutateAsync({
+ paymentToken: paymentMethod.id,
+ paymentType: PaymentMethodType.CREDIT_CARD,
+ paymentProvider: PaymentProvider.STRIPE,
+ });
+ } catch (err: unknown) {
+ if (err instanceof GraphQLErrorWithCodes) {
+ setCheckoutErrors(err.codes);
+ }
+ // Other errors are silently ignored
}
- // Other errors are silently ignored
+ } else {
+ setCheckoutErrors(['TRANSACTION_PROCESSING_FAILED']);
}
- } else {
- setCheckoutErrors(['payment_method_creation_failed']);
}
- }
- if (mode === 'express') {
- const { error, confirmationToken: expressToken } =
- await stripe.createConfirmationToken({
+ if (mode === 'express') {
+ const { error, paymentMethod } = await stripe.createPaymentMethod({
elements,
- params: {
- ...stripePaymentExpressRequest,
- },
+ params: stripePaymentExpressRequest,
});
- if (error) {
- setCheckoutErrors([
- error.code || 'confirmation_token_creation_failed',
- ]);
- return;
- }
+ if (error) {
+ setCheckoutErrors([error.code || 'TRANSACTION_PROCESSING_FAILED']);
+ return;
+ }
+
+ if (paymentMethod) {
+ try {
+ // Build the checkout body similar to godaddy.tsx
+ const event = expressData?.event;
+ const currencyCode =
+ expressData?.shippingTotal?.currencyCode || 'USD';
+
+ const walletType = paymentMethod.card?.wallet?.type;
+ const paymentType =
+ walletType || event?.expressPaymentType || 'card';
- if (expressToken) {
- try {
- await confirmCheckout.mutateAsync({
- paymentToken: expressToken.id,
- paymentType: expressToken?.payment_method_preview?.type,
- paymentProvider: PaymentProvider.STRIPE,
- });
- } catch (err: unknown) {
- if (err instanceof GraphQLErrorWithCodes) {
- setCheckoutErrors(err.codes);
+ // Map Stripe billing details to checkout format
+ const billing = event?.billingDetails
+ ? {
+ email: event.billingDetails.email || '',
+ phone: event.billingDetails.phone || '',
+ firstName: event.billingDetails.name?.split(' ')?.[0] || '',
+ lastName:
+ event.billingDetails.name
+ ?.split(' ')
+ .slice(1)
+ .join(' ') || '',
+ address: {
+ countryCode: event.billingDetails.address?.country || '',
+ postalCode:
+ event.billingDetails.address?.postal_code || '',
+ adminArea1: event.billingDetails.address?.state || '',
+ adminArea2: event.billingDetails.address?.city || '',
+ addressLine1: event.billingDetails.address?.line1 || '',
+ addressLine2: event.billingDetails.address?.line2 || '',
+ },
+ }
+ : undefined;
+
+ // Map Stripe shipping address to checkout format
+ const shipping = event?.shippingAddress
+ ? {
+ email: event.billingDetails?.email || '',
+ phone: event.billingDetails?.phone || '',
+ firstName:
+ event.shippingAddress.name?.split(' ')?.[0] || '',
+ lastName:
+ event.shippingAddress.name
+ ?.split(' ')
+ .slice(1)
+ .join(' ') || '',
+ address: {
+ countryCode: event.shippingAddress.address?.country || '',
+ postalCode:
+ event.shippingAddress.address?.postal_code || '',
+ adminArea1: event.shippingAddress.address?.state || '',
+ adminArea2: event.shippingAddress.address?.city || '',
+ addressLine1: event.shippingAddress.address?.line1 || '',
+ addressLine2: event.shippingAddress.address?.line2 || '',
+ },
+ }
+ : undefined;
+
+ // Build shipping lines from selected shipping method
+ const shippingLines = expressData?.selectedShippingMethod
+ ? [
+ {
+ amount: expressData.shippingTotal || {
+ currencyCode,
+ value: 0,
+ },
+ name:
+ expressData.selectedShippingMethod.displayName || '',
+ requestedProvider:
+ expressData.selectedShippingMethod.carrierCode || '',
+ requestedService:
+ expressData.selectedShippingMethod.serviceCode || '',
+ totals: {
+ subTotal: expressData.shippingTotal || {
+ currencyCode,
+ value: 0,
+ },
+ taxTotal: {
+ value: 0,
+ currencyCode,
+ },
+ },
+ },
+ ]
+ : undefined;
+
+ await confirmCheckout.mutateAsync({
+ paymentToken: paymentMethod.id,
+ paymentType,
+ paymentProvider: PaymentProvider.STRIPE,
+ isExpress: true,
+ // Include shipping total if available
+ ...(expressData?.shippingTotal
+ ? { shippingTotal: expressData.shippingTotal }
+ : {}),
+ // Include calculated taxes if available
+ ...(expressData?.calculatedTaxes
+ ? { calculatedTaxes: expressData.calculatedTaxes }
+ : {}),
+ // Include calculated adjustments (discounts) if available
+ ...(expressData?.calculatedAdjustments
+ ? { calculatedAdjustments: expressData.calculatedAdjustments }
+ : {}),
+ // Include billing address if available
+ ...(billing ? { billing } : {}),
+ // Include shipping address if available
+ ...(shipping ? { shipping } : {}),
+ // Include shipping lines if available
+ ...(shippingLines ? { shippingLines } : {}),
+ });
+ } catch (err: unknown) {
+ if (err instanceof GraphQLErrorWithCodes) {
+ setCheckoutErrors(err.codes);
+ }
+ throw err; // Re-throw so caller can handle
}
- // Other errors are silently ignored
+ } else {
+ setCheckoutErrors(['TRANSACTION_PROCESSING_FAILED']);
}
- } else {
- setCheckoutErrors(['confirmation_token_creation_failed']);
}
- }
- return { success: false, error: `Mode not supported: ${mode}` };
- } finally {
- setIsProcessingPayment(false);
- }
- }, [
- mode,
- stripe,
- elements,
- stripePaymentExpressRequest,
- confirmCheckout.mutateAsync,
- setCheckoutErrors,
- ]);
+ return { success: false, error: `Mode not supported: ${mode}` };
+ } finally {
+ setIsProcessingPayment(false);
+ }
+ },
+ [mode, stripe, elements, confirmCheckout.mutateAsync, setCheckoutErrors]
+ );
return {
handleSubmit,