CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stripe--react-stripe-js

React components and hooks for Stripe.js and Elements payment processing integration

Pending
Overview
Eval results
Files

embedded-checkout.mddocs/

Embedded Checkout

Complete embedded checkout solution for streamlined payment experiences. Provides a fully-hosted checkout flow that can be embedded directly into your application.

Capabilities

EmbeddedCheckoutProvider

Context provider that manages the embedded checkout instance and configuration throughout your React application.

/**
 * React context provider for Embedded Checkout
 * @param props - Embedded checkout provider configuration props
 * @returns JSX element wrapping child components with embedded checkout context
 */
function EmbeddedCheckoutProvider(props: EmbeddedCheckoutProviderProps): JSX.Element;

interface EmbeddedCheckoutProviderProps {
  /** Stripe instance or Promise resolving to Stripe instance */
  stripe: PromiseLike<Stripe | null> | Stripe | null;
  /** Embedded checkout configuration options */
  options: EmbeddedCheckoutOptions;
  /** Child components that will have access to embedded checkout context */
  children: ReactNode;
}

interface EmbeddedCheckoutOptions {
  /** Client secret for the checkout session */
  clientSecret?: string | null;
  /** Function to fetch client secret dynamically */
  fetchClientSecret?: (() => Promise<string>) | null;
  /** Callback fired when checkout is completed successfully */
  onComplete?: () => void;
  /** Callback fired when checkout encounters an error */
  onError?: (error: {message: string}) => void;
  /** Callback fired when shipping details change */
  onShippingDetailsChange?: (
    event: StripeEmbeddedCheckoutShippingDetailsChangeEvent
  ) => Promise<ResultAction>;
  /** Callback fired when line items change */
  onLineItemsChange?: (
    event: StripeEmbeddedCheckoutLineItemsChangeEvent
  ) => Promise<ResultAction>;
}

EmbeddedCheckout

The embedded checkout component that renders the complete checkout experience within your application.

/**
 * Embedded checkout component that mounts the checkout experience
 * @param props - Embedded checkout component props
 * @returns JSX element for embedded checkout interface
 */
function EmbeddedCheckout(props: EmbeddedCheckoutProps): JSX.Element;

interface EmbeddedCheckoutProps {
  /** HTML id attribute for the checkout container */
  id?: string;
  /** CSS class name for the checkout container */
  className?: string;
}

Basic Usage Examples:

import React, { useState, useCallback } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
  EmbeddedCheckoutProvider,
  EmbeddedCheckout
} from '@stripe/react-stripe-js';

const stripePromise = loadStripe('pk_test_...');

const CheckoutForm = () => {
  const [clientSecret, setClientSecret] = useState('');

  // Fetch client secret from your backend
  const fetchClientSecret = useCallback(async () => {
    const response = await fetch('/api/create-checkout-session', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        // Your checkout session parameters
        line_items: [
          {
            price: 'price_1234',
            quantity: 1,
          },
        ],
      }),
    });

    const { client_secret } = await response.json();
    return client_secret;
  }, []);

  const handleComplete = () => {
    console.log('Checkout completed successfully!');
    // Redirect to success page or show confirmation
    window.location.href = '/checkout/success';
  };

  const handleError = (error) => {
    console.error('Checkout error:', error.message);
    // Handle checkout errors
  };

  return (
    <div className="checkout-container">
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{
          fetchClientSecret,
          onComplete: handleComplete,
          onError: handleError
        }}
      >
        <EmbeddedCheckout className="embedded-checkout" />
      </EmbeddedCheckoutProvider>
    </div>
  );
};

export default CheckoutForm;

Advanced Usage with Static Client Secret:

import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
  EmbeddedCheckoutProvider,
  EmbeddedCheckout
} from '@stripe/react-stripe-js';

const stripePromise = loadStripe('pk_test_...');

const StaticCheckoutForm = ({ checkoutSessionClientSecret }) => {
  const handleComplete = () => {
    // Handle successful checkout
    console.log('Payment completed!');
    
    // You might want to:
    // - Show a success message
    // - Redirect to a confirmation page
    // - Update your app state
    // - Send analytics events
  };

  const handleError = (error) => {
    // Handle checkout errors
    console.error('Checkout failed:', error.message);
    
    // You might want to:
    // - Show an error message to the user
    // - Log the error for debugging
    // - Provide alternative payment options
    // - Contact support information
  };

  return (
    <div className="checkout-page">
      <div className="checkout-header">
        <h1>Complete Your Purchase</h1>
        <p>Secure checkout powered by Stripe</p>
      </div>
      
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{
          clientSecret: checkoutSessionClientSecret,
          onComplete: handleComplete,
          onError: handleError
        }}
      >
        <EmbeddedCheckout 
          id="checkout-form"
          className="my-embedded-checkout" 
        />
      </EmbeddedCheckoutProvider>
      
      <div className="checkout-footer">
        <p>Questions? Contact support@example.com</p>
      </div>
    </div>
  );
};

Server-Side Rendering (SSR) Example:

import React, { useState, useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
  EmbeddedCheckoutProvider,
  EmbeddedCheckout
} from '@stripe/react-stripe-js';

// SSR-safe component
const SSRCheckoutForm = ({ initialClientSecret = null }) => {
  const [stripePromise] = useState(() => 
    typeof window !== 'undefined' ? loadStripe('pk_test_...') : null
  );
  const [clientSecret, setClientSecret] = useState(initialClientSecret);

  useEffect(() => {
    if (!clientSecret && typeof window !== 'undefined') {
      // Fetch client secret on client-side mount
      fetchCheckoutSession();
    }
  }, [clientSecret]);

  const fetchCheckoutSession = async () => {
    try {
      const response = await fetch('/api/create-checkout-session', {
        method: 'POST',
      });
      const { client_secret } = await response.json();
      setClientSecret(client_secret);
    } catch (error) {
      console.error('Failed to create checkout session:', error);
    }
  };

  const fetchClientSecret = async () => {
    return clientSecret || fetchCheckoutSession();
  };

  // Don't render checkout on server
  if (typeof window === 'undefined') {
    return (
      <div className="checkout-loading">
        <div>Loading checkout...</div>
      </div>
    );
  }

  // Don't render without Stripe or client secret
  if (!stripePromise || !clientSecret) {
    return (
      <div className="checkout-loading">
        <div>Preparing checkout...</div>
      </div>
    );
  }

  return (
    <EmbeddedCheckoutProvider
      stripe={stripePromise}
      options={{
        clientSecret,
        onComplete: () => {
          window.location.href = '/success';
        },
        onError: (error) => {
          console.error('Checkout error:', error);
        }
      }}
    >
      <EmbeddedCheckout />
    </EmbeddedCheckoutProvider>
  );
};

Custom Styling and Integration:

import React, { useState } from 'react';
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';

const CustomizedCheckout = ({ stripePromise, clientSecret }) => {
  const [isLoading, setIsLoading] = useState(true);
  const [checkoutError, setCheckoutError] = useState(null);

  const handleComplete = () => {
    setIsLoading(false);
    // Custom success handling
    console.log('Checkout completed!');
    
    // Example: Show success animation, then redirect
    setTimeout(() => {
      window.location.href = '/order-confirmation';
    }, 2000);
  };

  const handleError = (error) => {
    setIsLoading(false);
    setCheckoutError(error.message);
    
    // Custom error handling
    console.error('Checkout failed:', error);
  };

  return (
    <div className="custom-checkout-container">
      <style jsx>{`
        .custom-checkout-container {
          max-width: 600px;
          margin: 0 auto;
          padding: 20px;
          background: #ffffff;
          border-radius: 12px;
          box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        
        .checkout-header {
          text-align: center;
          margin-bottom: 30px;
        }
        
        .checkout-title {
          font-size: 24px;
          font-weight: bold;
          color: #333;
          margin-bottom: 8px;
        }
        
        .checkout-subtitle {
          color: #666;
          font-size: 16px;
        }
        
        .embedded-checkout {
          min-height: 400px;
          border-radius: 8px;
          overflow: hidden;
        }
        
        .loading-indicator {
          display: flex;
          justify-content: center;
          align-items: center;
          height: 200px;
          color: #666;
        }
        
        .error-message {
          background: #fee;
          border: 1px solid #fcc;
          color: #c00;
          padding: 12px;
          border-radius: 6px;
          margin-bottom: 20px;
        }
      `}</style>
      
      <div className="checkout-header">
        <h1 className="checkout-title">Secure Checkout</h1>
        <p className="checkout-subtitle">
          Complete your purchase securely with Stripe
        </p>
      </div>
      
      {checkoutError && (
        <div className="error-message">
          <strong>Checkout Error:</strong> {checkoutError}
          <button 
            onClick={() => window.location.reload()}
            style={{ marginLeft: '10px' }}
          >
            Try Again
          </button>
        </div>
      )}
      
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{
          clientSecret,
          onComplete: handleComplete,
          onError: handleError
        }}
      >
        <EmbeddedCheckout className="embedded-checkout" />
      </EmbeddedCheckoutProvider>
      
      {isLoading && (
        <div className="loading-indicator">
          Loading secure checkout...
        </div>
      )}
    </div>
  );
};

Integration with React Router:

import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { loadStripe } from '@stripe/stripe-js';
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';

const stripePromise = loadStripe('pk_test_...');

const CheckoutPage = () => {
  const { sessionId } = useParams();
  const navigate = useNavigate();
  const [clientSecret, setClientSecret] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (sessionId) {
      // Retrieve existing checkout session
      fetchExistingSession(sessionId);
    } else {
      // Create new checkout session
      createNewSession();
    }
  }, [sessionId]);

  const fetchExistingSession = async (sessionId) => {
    try {
      const response = await fetch(`/api/checkout-sessions/${sessionId}`);
      const session = await response.json();
      setClientSecret(session.client_secret);
    } catch (err) {
      setError('Failed to load checkout session');
    }
  };

  const createNewSession = async () => {
    try {
      const response = await fetch('/api/create-checkout-session', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          // session parameters
        }),
      });
      const { client_secret } = await response.json();
      setClientSecret(client_secret);
    } catch (err) {
      setError('Failed to create checkout session');
    }
  };

  const handleComplete = () => {
    // Navigate to success page
    navigate('/checkout/success');
  };

  const handleError = (error) => {
    console.error('Checkout error:', error);
    navigate('/checkout/error', { state: { error: error.message } });
  };

  if (error) {
    return <div className="error">Error: {error}</div>;
  }

  if (!clientSecret) {
    return <div className="loading">Loading checkout...</div>;
  }

  return (
    <div className="checkout-page">
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{
          clientSecret,
          onComplete: handleComplete,
          onError: handleError
        }}
      >
        <EmbeddedCheckout />
      </EmbeddedCheckoutProvider>
    </div>
  );
};

export default CheckoutPage;

Context Hook Usage

/**
 * Hook to access embedded checkout context
 * @returns Embedded checkout context value
 */
function useEmbeddedCheckoutContext(): EmbeddedCheckoutContextValue;

interface EmbeddedCheckoutContextValue {
  /** Embedded checkout instance */
  embeddedCheckout: EmbeddedCheckoutPublicInterface | null;
}

interface EmbeddedCheckoutPublicInterface {
  /** Mount the checkout to a DOM element */
  mount(location: string | HTMLElement): void;
  /** Unmount the checkout from its current location */
  unmount(): void;
  /** Destroy the checkout instance */
  destroy(): void;
}

Custom Hook Usage Example:

import React, { useEffect, useRef } from 'react';
import { useEmbeddedCheckoutContext } from '@stripe/react-stripe-js';

const CustomCheckoutComponent = () => {
  const { embeddedCheckout } = useEmbeddedCheckoutContext();
  const checkoutRef = useRef(null);

  useEffect(() => {
    if (embeddedCheckout && checkoutRef.current) {
      // Manually mount the checkout
      embeddedCheckout.mount(checkoutRef.current);
      
      return () => {
        // Cleanup on unmount
        if (embeddedCheckout) {
          try {
            embeddedCheckout.unmount();
          } catch (error) {
            // Handle unmount errors silently
          }
        }
      };
    }
  }, [embeddedCheckout]);

  return (
    <div>
      <h2>Custom Checkout Implementation</h2>
      <div ref={checkoutRef} className="custom-checkout-mount" />
    </div>
  );
};

Error Handling

The embedded checkout automatically handles most errors, but you can provide custom error handling:

const CheckoutWithErrorHandling = () => {
  const [errors, setErrors] = useState([]);
  
  const handleError = (error) => {
    setErrors(prev => [...prev, {
      id: Date.now(),
      message: error.message,
      timestamp: new Date().toISOString()
    }]);
    
    // Log to your error tracking service
    console.error('Embedded checkout error:', error);
  };

  const clearErrors = () => setErrors([]);

  return (
    <div>
      {errors.length > 0 && (
        <div className="error-panel">
          <h3>Checkout Errors</h3>
          {errors.map(error => (
            <div key={error.id} className="error-item">
              {error.message}
              <small>{error.timestamp}</small>
            </div>
          ))}
          <button onClick={clearErrors}>Clear Errors</button>
        </div>
      )}
      
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{
          clientSecret,
          onError: handleError
        }}
      >
        <EmbeddedCheckout />
      </EmbeddedCheckoutProvider>
    </div>
  );
};

Install with Tessl CLI

npx tessl i tessl/npm-stripe--react-stripe-js

docs

address-elements.md

bank-elements.md

card-elements.md

checkout-components.md

core-providers-hooks.md

embedded-checkout.md

index.md

message-elements.md

payment-elements.md

tile.json