Skip to main content

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

NameTypeRequiredDescription
placeOrderApistringYesAPI endpoint for placing order
checkoutSuccessUrlstringYesSuccess page URL
allowGuestCheckoutbooleanNoAllow guest checkout (default: false)
formUseFormReturnYesReact Hook Form instance
enableForm() => voidYesEnable form callback
disableForm() => voidYesDisable form callback
childrenReactNodeYesChild components

useCheckout Hook

Returns checkout state:

PropertyTypeDescription
orderPlacedbooleanTrue when order placed successfully
orderIdstringPlaced order ID
loadingbooleanTrue when placing order
cartIdstringCurrent cart UUID
checkoutSuccessUrlstringSuccess page URL
requiresShipmentbooleanTrue if items need shipping
allowGuestCheckoutbooleanGuest checkout allowed
checkoutDataCheckoutDataCurrent checkout data
formUseFormReturnForm instance
registeredPaymentComponentsRecord<string, Component>Payment method renderers

useCheckoutDispatch Hook

Returns checkout actions:

MethodSignatureDescription
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) => voidReplace checkout data
updateCheckoutData(data: Partial<CheckoutData>) => voidUpdate checkout data
clearCheckoutData() => voidClear all checkout data
registerPaymentComponent(code, component) => voidRegister payment method renderer
enableForm() => voidEnable checkout form
disableForm() => voidDisable 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