CheckoutContext
Description
Manages checkout process state and operations. Handles order placement, shipping/payment methods, checkout data, and form state. Integrates with cart context for checkout flow.
Import
import { CheckoutProvider, useCheckout, useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
Usage
Setup Provider
import { CheckoutProvider } from '@components/frontStore/checkout/CheckoutContext';
import { FormProvider, useForm } from 'react-hook-form';
function CheckoutPage() {
const methods = useForm();
const [formEnabled, setFormEnabled] = useState(true);
return (
<FormProvider {...methods}>
<CheckoutProvider
placeOrderApi="/api/placeOrder"
checkoutSuccessUrl="/checkout/success"
allowGuestCheckout={true}
form={methods}
enableForm={() => setFormEnabled(true)}
disableForm={() => setFormEnabled(false)}
>
{/* Checkout components */}
</CheckoutProvider>
</FormProvider>
);
}
Access Checkout State
import { useCheckout } from '@components/frontStore/checkout/CheckoutContext';
function CheckoutSummary() {
const { loading, requiresShipment, orderPlaced, orderId } = useCheckout();
if (orderPlaced) {
return <div>Order {orderId} placed successfully!</div>;
}
return (
<div>
{loading && <div>Processing...</div>}
{requiresShipment && <div>Shipping required</div>}
</div>
);
}
Use Checkout Actions
import { useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
function CheckoutButton() {
const { checkout } = useCheckoutDispatch();
const handleCheckout = async () => {
try {
const order = await checkout();
window.location.href = `/checkout/success/${order.uuid}`;
} catch (error) {
alert(error.message);
}
};
return (
<button onClick={handleCheckout}>
Place Order
</button>
);
}
API
CheckoutProvider Props
| Name | Type | Required | Description |
|---|---|---|---|
| placeOrderApi | string | Yes | API endpoint for placing order |
| checkoutSuccessUrl | string | Yes | Success page URL |
| allowGuestCheckout | boolean | No | Allow guest checkout (default: false) |
| form | UseFormReturn | Yes | React Hook Form instance |
| enableForm | () => void | Yes | Enable form callback |
| disableForm | () => void | Yes | Disable form callback |
| children | ReactNode | Yes | Child components |
useCheckout Hook
Returns checkout state:
| Property | Type | Description |
|---|---|---|
| orderPlaced | boolean | True when order placed successfully |
| orderId | string | Placed order ID |
| loading | boolean | True when placing order |
| cartId | string | Current cart UUID |
| checkoutSuccessUrl | string | Success page URL |
| requiresShipment | boolean | True if items need shipping |
| allowGuestCheckout | boolean | Guest checkout allowed |
| checkoutData | CheckoutData | Current checkout data |
| form | UseFormReturn | Form instance |
| registeredPaymentComponents | Record<string, Component> | Payment method renderers |
useCheckoutDispatch Hook
Returns checkout actions:
| Method | Signature | Description |
|---|---|---|
| placeOrder | () => Promise<any> | Place order (expects data in cart) |
| checkout | () => Promise<any> | Checkout with all data submission |
| getPaymentMethods | () => PaymentMethod[] | Get available payment methods |
| getShippingMethods | (params?) => Promise<ShippingMethod[]> | Get shipping methods |
| setCheckoutData | (data: CheckoutData) => void | Replace checkout data |
| updateCheckoutData | (data: Partial<CheckoutData>) => void | Update checkout data |
| clearCheckoutData | () => void | Clear all checkout data |
| registerPaymentComponent | (code, component) => void | Register payment method renderer |
| enableForm | () => void | Enable checkout form |
| disableForm | () => void | Disable checkout form |
Type Definitions
PaymentMethod
interface PaymentMethod {
code: string;
name: string;
[key: string]: any; // Extended fields
}
ShippingMethod
interface ShippingMethod {
code: string;
name: string;
cost?: {
value: number;
text: string;
};
[key: string]: any; // Extended fields
}
ShippingAddressParams
interface ShippingAddressParams {
country: string;
province?: string;
postcode?: string;
}
PaymentMethodComponent
interface PaymentMethodComponent {
nameRenderer: React.ComponentType<{ isSelected: boolean }>;
formRenderer: React.ComponentType<{ isSelected: boolean }>;
checkoutButtonRenderer: React.ComponentType<{ isSelected: boolean }>;
}
Examples
Basic Checkout Flow
import { CheckoutProvider, useCheckout, useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
import { FormProvider, useForm } from 'react-hook-form';
function CheckoutPage() {
const methods = useForm();
const [formEnabled, setFormEnabled] = useState(true);
return (
<FormProvider {...methods}>
<CheckoutProvider
placeOrderApi="/api/placeOrder"
checkoutSuccessUrl="/checkout/success"
form={methods}
enableForm={() => setFormEnabled(true)}
disableForm={() => setFormEnabled(false)}
>
<CheckoutForm />
</CheckoutProvider>
</FormProvider>
);
}
function CheckoutForm() {
const { loading, orderPlaced } = useCheckout();
const { checkout } = useCheckoutDispatch();
const handleSubmit = async () => {
try {
await checkout();
} catch (error) {
console.error(error);
}
};
if (orderPlaced) {
return <div>Order placed!</div>;
}
return (
<button onClick={handleSubmit} disabled={loading}>
{loading ? 'Processing...' : 'Place Order'}
</button>
);
}
Payment Methods
import { useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
import { useEffect, useState } from 'react';
function PaymentMethods() {
const { getPaymentMethods } = useCheckoutDispatch();
const [methods, setMethods] = useState([]);
useEffect(() => {
const available = getPaymentMethods();
setMethods(available);
}, []);
return (
<div className="payment-methods">
<h3>Payment Method</h3>
{methods.map(method => (
<label key={method.code}>
<input type="radio" name="payment" value={method.code} />
{method.name}
</label>
))}
</div>
);
}
Shipping Methods
import { useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
import { useState, useEffect } from 'react';
function ShippingMethods({ address }) {
const { getShippingMethods } = useCheckoutDispatch();
const [methods, setMethods] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (address) {
setLoading(true);
getShippingMethods({
country: address.country,
province: address.province,
postcode: address.postcode
})
.then(setMethods)
.finally(() => setLoading(false));
}
}, [address]);
return (
<div className="shipping-methods">
<h3>Shipping Method</h3>
{loading && <div>Loading...</div>}
{methods.map(method => (
<label key={method.code}>
<input type="radio" name="shipping" value={method.code} />
{method.name} - {method.cost?.text || 'Free'}
</label>
))}
</div>
);
}
Checkout Data Management
import { useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
function CheckoutForm() {
const { updateCheckoutData, checkout } = useCheckoutDispatch();
const handleAddressChange = (address) => {
updateCheckoutData({
shipping_address: address
});
};
const handlePaymentChange = (paymentCode) => {
updateCheckoutData({
payment_method: paymentCode
});
};
const handleSubmit = async () => {
try {
await checkout();
} catch (error) {
alert(error.message);
}
};
return (
<form>
{/* Address fields */}
{/* Payment selection */}
<button type="button" onClick={handleSubmit}>
Place Order
</button>
</form>
);
}
Register Payment Component
import { useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
import { useEffect } from 'react';
function PaymentMethodName({ isSelected }) {
return <span>Credit Card</span>;
}
function PaymentMethodForm({ isSelected }) {
if (!isSelected) return null;
return (
<div>
<input placeholder="Card Number" />
<input placeholder="Expiry" />
<input placeholder="CVV" />
</div>
);
}
function CheckoutButton({ isSelected }) {
if (!isSelected) return null;
return <button>Pay with Card</button>;
}
function CreditCardPayment() {
const { registerPaymentComponent } = useCheckoutDispatch();
useEffect(() => {
registerPaymentComponent('credit_card', {
nameRenderer: PaymentMethodName,
formRenderer: PaymentMethodForm,
checkoutButtonRenderer: CheckoutButton
});
}, []);
return null;
}
Complete Checkout Page
import { CheckoutProvider, useCheckout, useCheckoutDispatch } from '@components/frontStore/checkout/CheckoutContext';
import { FormProvider, useForm } from 'react-hook-form';
import { useState } from 'react';
function CheckoutPage() {
const methods = useForm();
const [formEnabled, setFormEnabled] = useState(true);
return (
<FormProvider {...methods}>
<CheckoutProvider
placeOrderApi="/api/placeOrder"
checkoutSuccessUrl="/checkout/success"
allowGuestCheckout={true}
form={methods}
enableForm={() => setFormEnabled(true)}
disableForm={() => setFormEnabled(false)}
>
<CheckoutContent formEnabled={formEnabled} />
</CheckoutProvider>
</FormProvider>
);
}
function CheckoutContent({ formEnabled }) {
const { loading, orderPlaced, orderId, requiresShipment } = useCheckout();
const {
getPaymentMethods,
getShippingMethods,
updateCheckoutData,
checkout
} = useCheckoutDispatch();
const handleSubmit = async () => {
try {
const order = await checkout();
window.location.href = `/checkout/success/${order.uuid}`;
} catch (error) {
alert(error.message);
}
};
if (orderPlaced) {
return (
<div className="success">
<h1>Order Placed!</h1>
<p>Order ID: {orderId}</p>
</div>
);
}
return (
<div className="checkout-page">
<h1>Checkout</h1>
{/* Shipping Address */}
{requiresShipment && (
<section>
<h2>Shipping Address</h2>
{/* Address form fields */}
</section>
)}
{/* Shipping Method */}
{requiresShipment && (
<section>
<h2>Shipping Method</h2>
{/* Shipping method selection */}
</section>
)}
{/* Payment Method */}
<section>
<h2>Payment Method</h2>
{/* Payment method selection */}
</section>
{/* Submit */}
<button
onClick={handleSubmit}
disabled={loading || !formEnabled}
className="checkout-btn"
>
{loading ? 'Processing...' : 'Place Order'}
</button>
</div>
);
}
Methods Details
placeOrder()
Places order with cart data already saved. Use when checkout data is already in cart context.
checkout()
Validates form, submits all checkout data, and places order. Use for complete checkout flow.
getShippingMethods(params?)
- Without params: Returns initial methods from cart
- With params: Fetches methods based on address
Features
- Order Placement: Two methods (placeOrder, checkout)
- Form Integration: React Hook Form support
- Payment Methods: Registry system for custom renderers
- Shipping Methods: Dynamic fetching based on address
- Checkout Data: Centralized data management
- Loading States: Tracks order placement progress
- Error Handling: Retry mechanism with exponential backoff
- Guest Checkout: Optional guest checkout support
- Form Control: Enable/disable form during processing
- Type Safe: Full TypeScript support
Requirements
- CartContext: Must be wrapped in cart provider
- FormProvider: Requires React Hook Form
- Cart ID: Cart must be initialized
Related Components
- CartContext - Shopping cart context