---
applyTo: 'src/app/**/checkout/**|src/views/checkout/**|src/components/**/checkout/**'
---

# Project Zero Checkout - Developer Instructions

## Overview

The checkout functionality in Project Zero Next.js is a multi-step e-commerce flow that handles user authentication, shipping, payment, and order completion. It's built with Redux state management, RTK Query for API calls, and supports various payment plugins.

## Checkout Architecture

### Route Structure

```
/[commerce]/[locale]/[currency]/orders/checkout/
```

- **Main Page**: `src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx`
- **Route Constants**: Defined in `src/routes/index.ts` as `ROUTES.CHECKOUT`

### Core Components

```
src/views/checkout/
├── auth.tsx                    # Authentication/Guest checkout
├── step-list.tsx              # Progress indicator
├── index.tsx                  # Summary component
└── steps/
    ├── shipping/              # Shipping step components
    └── payment/               # Payment step components
```

### State Management

**Redux Store**: `checkout` slice handles multi-step flow state

```tsx
import { useAppSelector, useAppDispatch } from '@akinon/next/redux/hooks';
import { setCurrentStep, resetCheckoutState } from '@akinon/next/redux/reducers/checkout';

// Access checkout state
const { steps, preOrder } = useAppSelector((state: RootState) => state.checkout);
```

## Checkout Flow Steps

### 1. Authentication Step

**Template Detection**: `checkoutData?.template_name === 'orders/index.html'`

**Components Used**:
- `CheckoutAuth` - Main authentication wrapper
- `Login` - Existing user login
- `GuestLogin` - Guest checkout option

**Key Features**:
- NextAuth.js integration with `useSession()`
- Callback URL handling for post-login redirect
- Guest checkout with email/phone validation

```tsx
// Authentication check pattern
const { status } = useSession();

useEffect(() => {
  if (status === 'authenticated') {
    dispatch(api.util.invalidateTags(['Checkout']));
  } else if (status === 'unauthenticated') {
    router.replace(ROUTES.CHECKOUT + `?callbackUrl=${ROUTES.CHECKOUT}`);
  }
}, [status]);
```

### 2. Shipping Step

**When Active**: `steps.current === CheckoutStep.Shipping`

**Key APIs**:
- `setDeliveryOption` - Select delivery method
- `setAddresses` - Set billing/shipping addresses
- `setShippingOption` - Choose shipping method

**Auto-progression**: Automatically moves to payment when shipping is completed

```tsx
useEffect(() => {
  if (steps.shipping.completed && !initialStepChanged.current) {
    dispatch(setCurrentStep(CheckoutStep.Payment));
    initialStepChanged.current = true;
  }
}, [steps.shipping.completed]);
```

### 3. Payment Step

**When Active**: `steps.current === CheckoutStep.Payment`

**Key APIs**:
- `setPaymentOption` - Select payment method
- `setInstallmentOption` - Choose installments
- `completeCreditCardPayment` - Process card payments
- Various plugin-specific payment completions

**Plugin Integration**: Extensive payment plugin support via `PluginModule`

## API Integration

### Core Hooks

```tsx
// Primary checkout data fetching
const { data: checkoutData, isFetching, isError, refetch } = useFetchCheckoutQuery(null);

// State reset on page load
const { data: indexData, isLoading } = useResetCheckoutStateQuery(null);
```

### Mutation Patterns

**All checkout mutations follow this pattern**:

```tsx
const [mutationFn] = useMutationHook();

// With loading state management
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
  dispatch(setShippingStepBusy(true)); // or setPaymentStepBusy
  await queryFulfilled;
  dispatch(setShippingStepBusy(false));
}
```

### Error Handling

**Standard error UI pattern**:

```tsx
if (isResetStateLoading || isFetching || isError) {
  return (
    <div className="flex flex-col items-center justify-center h-80">
      {isResetStateLoading || isFetching ? (
        <LoaderSpinner />
      ) : (
        <>
          <div>{t('checkout.error.title')}</div>
          <div className="mt-5">
            <Button onClick={refetchCheckout}>
              {t('checkout.error.button')}
            </Button>
          </div>
        </>
      )}
    </div>
  );
}
```

## Plugin System

### Core Plugin Architecture

**Plugin Wrapper**: All checkout features use `PluginModule` for extensibility

```tsx
import PluginModule, { Component } from '@akinon/next/components/plugin-module';

// Conditional plugin rendering
<PluginModule component={Component.PluginName} props={...} />
```

### Available Checkout Plugins

**Payment Plugins**:
- `pz-masterpass` - Masterpass integration
- `pz-apple-pay` - Apple Pay support (with custom UI override capabilities)
- `pz-gpay` - Google Pay integration
- `pz-bkm` - BKM Express payments
- `pz-saved-card` - Saved card management
- `pz-credit-payment` - Credit/installment payments
- `pz-flow-payment` - Flow payment gateway
- `pz-tabby-extension` - Tabby BNPL (MENA)
- `pz-tamara-extension` - Tamara BNPL (MENA)

**Checkout Enhancement Plugins**:
- `pz-one-click-checkout` - Simplified one-click payments
- `pz-checkout-gift-pack` - Gift wrapping options
- `pz-click-collect` - Store pickup options
- `pz-otp` - OTP verification
- `pz-pay-on-delivery` - Cash on delivery

### Plugin Integration Example

```tsx
// Masterpass integration in checkout
<PluginModule component={Component.MasterpassProvider}>
  <PluginModule component={Component.MasterpassDeleteConfirmationModal} />
  <PluginModule component={Component.MasterpassOtpModal} />
  <PluginModule component={Component.MasterpassLinkModal} />
  
  {/* Your checkout content */}
</PluginModule>
```

**Apple Pay Implementation**:

There are two ways to implement Apple Pay:

### Option 1: Full Plugin (Recommended)
Use the `pz-apple-pay` package for complete Apple Pay functionality:

**Quick Setup** (Recommended):
```bash
npx @akinon/projectzero@latest --plugins
```

**Manual Setup**:

### Option 2: UI Customization Only
If you only need to customize the Apple Pay UI, create a custom component:

1. **Create the Apple Pay component**: Create `src/views/checkout/steps/payment/options/apple-pay.tsx`
2. **Import in payment.tsx**: Add `import ApplePay from './options/apple-pay';`
3. **Add to PaymentOptionViews**: Add the component to the PaymentOptionViews array

**Implementation Pattern**:
```tsx
// In payment.tsx
import ApplePay from './options/apple-pay';

export const PaymentOptionViews: Array<CheckoutPaymentOption> = [
  {
    slug: 'apple-pay-wallet',
    view: ApplePay
  }
];
```

### Payment UI Customization Patterns

**Apple Pay Component Structure**:

Here's the basic structure for the Apple Pay component that should be placed in `src/views/checkout/steps/payment/options/apple-pay.tsx`:

```tsx
'use client';

import { checkoutApi } from '@akinon/next/data/client/checkout';
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
import { Button, Modal } from '@theme/components';
import { RootState } from '@theme/redux/store';
import { useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useLocalization } from '@akinon/next/hooks';

export default function ApplePay() {
  const { handleSubmit } = useForm();
  const { t } = useLocalization();
  const { walletPaymentData, preOrder } = useAppSelector(
    (state: RootState) => state.checkout
  );
  
  const dispatch = useAppDispatch();
  const [errors, setErrors] = useState(null);
  const [showConfirmationModal, setShowConfirmationModal] = useState(false);

  const onSubmit: SubmitHandler<null> = async () => {
    try {
      // 1. Prepare payment request
      const request = new PaymentRequest(paymentMethodData, paymentDetails);
      
      // 2. Show confirmation modal instead of directly calling request.show()
      setShowConfirmationModal(true);
      
      // 3. Store request for later use
      window.applePayRequest = request;
    } catch (error) {
      setErrors(error);
    }
  };

  const handleApplePayConfirm = async () => {
    setShowConfirmationModal(false);
    
    try {
      const request = window.applePayRequest;
      
      // 4. Now trigger the actual Apple Pay flow
      request.show().then(async (paymentRequestResponse) => {
        // 5. Handle payment response and API calls
        // - Call setWalletPaymentPage
        // - Call setWalletCompletePage  
        // - Handle success/failure
        // - Redirect to thank you page
      });
    } catch (error) {
      setErrors(error);
    }
  };

  if (!walletPaymentData) {
    return null;
  }

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Button className="!hidden" type="submit"></Button>
      </form>
      
      {/* Custom confirmation modal */}
      {showConfirmationModal && (
        <Modal
          open={showConfirmationModal}
          setOpen={setShowConfirmationModal}
        >
          <div className="p-6">
            <p>{t('checkout.payment.apple_pay.confirmation_message')}</p>
            <Button onClick={handleApplePayConfirm}>
              {t('common.confirm')}
            </Button>
          </div>
        </Modal>
      )}
    </>
  );
}
```

**Key Implementation Steps**:

1. **Payment Request Setup**: Create PaymentRequest with proper method data and details
2. **Confirmation Modal**: Show custom UI before triggering native Apple Pay
3. **Request Storage**: Store payment request for later use via `window.applePayRequest`
4. **Payment Flow**: Handle the complete Apple Pay flow with API integrations
5. **Error Handling**: Implement comprehensive error handling throughout the process
6. **State Management**: Use Redux for checkout state and payment session management


## Form Handling

### Standard Form Pattern

**Use React Hook Form + Yup validation**:

```tsx
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

const schema = (t) => yup.object().shape({
  email: yup.string().email().required(t('error.required')),
  phone: yup.string().required(t('error.required'))
});

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: yupResolver(schema(t))
});
```

### Guest Login Form Example

```tsx
const onSubmit = async (data) => {
  try {
    await guestLogin(data);
    // Handle success - usually redirects automatically
  } catch (error) {
    // Handle validation errors
    if (error?.data?.form_errors) {
      // Set form errors from backend
    }
  }
};
```

## Internationalization

### Translation Keys

**Checkout-specific translations**: `checkout.json`

```tsx
import { useLocalization } from '@akinon/next/hooks';

const { t } = useLocalization();

// Common translation patterns
t('checkout.auth.title')           // "Don't have an account?"
t('checkout.auth.signup')          // "Sign up"
t('checkout.auth.guest_checkout')  // "Guest Checkout"
t('checkout.error.title')          // "Error occurred"
t('checkout.error.button')         // "Try Again"
```

### Multi-language Support

**Route structure includes locale**: `/[commerce]/[locale]/[currency]/orders/checkout/`

## Analytics Integration

### GTM/E-commerce Tracking

**Automatic event tracking**:

```tsx
import { pushAddShippingInfo, pushAddPaymentInfo } from '@theme/utils/gtm';

// Auto-triggered on step completion
useEffect(() => {
  if (isSuccess) {
    const products = checkoutData?.pre_order?.basket?.basketitem_set.map(
      (basketItem) => ({ ...basketItem.product })
    );
    
    if (steps.current === 'shipping') {
      pushAddShippingInfo(products);
    }
    if (steps.current === 'payment') {
      pushAddPaymentInfo(products, String(preOrder?.payment_option?.name));
    }
  }
}, [isSuccess, steps, checkoutData, preOrder?.payment_option?.name]);
```

## Development Guidelines

### Component Structure

**Follow this hierarchy**:

```tsx
// Page-level component (src/app/.../checkout/page.tsx)
const Checkout = () => {
  // 1. Hooks and state
  const { t } = useLocalization();
  const { steps, preOrder } = useAppSelector((state) => state.checkout);
  
  // 2. API calls
  const { data: checkoutData } = useFetchCheckoutQuery(null);
  
  // 3. Effects for flow control
  useEffect(() => {
    // Step progression logic
  }, [dependencies]);
  
  // 4. Early returns for different states
  if (isAuthRequired) return <CheckoutAuth />;
  if (isLoading) return <LoaderSpinner />;
  if (isError) return <ErrorState />;
  
  // 5. Main render with plugin wrappers
  return (
    <PluginModule component={Component.PaymentProvider}>
      <CheckoutStepList />
      <div className="checkout-content">
        {steps.current === CheckoutStep.Shipping && <ShippingStep />}
        {steps.current === CheckoutStep.Payment && <PaymentStep />}
      </div>
      <Summary />
    </PluginModule>
  );
};
```

### Dynamic Imports

**Use dynamic imports for step components**:

```tsx
const CheckoutAuth = useMemo(
  () => dynamic(() => import('@theme/views/checkout/auth')),
  []
);
```

### State Management Best Practices

**1. Always reset checkout state on unmount**:

```tsx
useEffect(() => {
  return () => {
    dispatch(resetCheckoutState());
  };
}, []);
```

**2. Use step-specific busy states**:

```tsx
// In mutations
dispatch(setShippingStepBusy(true));
// or
dispatch(setPaymentStepBusy(true));
```

**3. Handle step progression carefully**:

```tsx
const initialStepChanged = useRef<boolean>(false);

useEffect(() => {
  if (shouldProgressToNextStep && !initialStepChanged.current) {
    dispatch(setCurrentStep(NextStep));
    initialStepChanged.current = true;
  }
}, [shouldProgressToNextStep]);
```

### Styling Guidelines

**Use TailwindCSS with responsive design**:

```tsx
<div className="container flex flex-col flex-wrap w-full px-4 md:px-0">
  <div className="w-full h-fit-content lg:w-2/3">
    {/* Main content */}
  </div>
  <div className="w-full h-fit-content mt-6 lg:w-1/3 lg:pl-8 lg:mt-0">
    {/* Sidebar */}
  </div>
</div>
```

## Security Considerations

### Authentication

- Always check authentication status before allowing checkout
- Handle callback URLs properly for post-login redirects
- Support both authenticated and guest checkout flows

### Data Validation

- Validate all form inputs on both client and server
- Use proper form validation with Yup schemas
- Handle backend validation errors gracefully

### Payment Security

- Never store sensitive payment data in frontend state
- Use secure payment provider integrations
- Implement proper 3D Secure flows when required

## Testing Guidelines

### Component Testing

**Test checkout flow components**:

```typescript
// Test authentication flow
describe('CheckoutAuth', () => {
  it('redirects authenticated users', () => {
    // Mock authenticated session
    // Verify redirect behavior
  });
  
  it('shows guest login for unauthenticated users', () => {
    // Mock unauthenticated session
    // Verify guest login form is shown
  });
});
```

### Integration Testing

**Test complete checkout flows**:

```typescript
describe('Checkout Flow', () => {
  it('completes guest checkout successfully', async () => {
    // 1. Add items to basket
    // 2. Navigate to checkout
    // 3. Complete guest login
    // 4. Fill shipping information
    // 5. Select payment method
    // 6. Complete order
  });
});
```

## Common Patterns

### Redirect Handling

```tsx
// Handle API redirects
if (checkoutData?.redirect_url?.includes('basket')) {
  router.push(ROUTES.BASKET);
  return null;
}
```

### Plugin Component Patterns

```tsx
// Conditional plugin usage
<PluginModule component={Component.PluginName} props={{ data }}>
  {/* Children components */}
</PluginModule>

// Multiple plugin modals
<PluginModule component={Component.MasterpassProvider}>
  <PluginModule component={Component.MasterpassDeleteConfirmationModal} />
  <PluginModule component={Component.MasterpassOtpModal} />
  {/* Main content */}
</PluginModule>
```

### Loading State Management

```tsx
// Standard loading pattern
if (isResetStateLoading || isFetching || isError) {
  return (
    <div className="flex flex-col items-center justify-center h-80">
      {isResetStateLoading || isFetching ? (
        <LoaderSpinner />
      ) : (
        <ErrorWithRetry onRetry={refetchCheckout} />
      )}
    </div>
  );
}
```

## Troubleshooting

### Common Issues

**1. Checkout state not resetting**:
- Ensure `resetCheckoutState()` is called on component unmount
- Check if `useResetCheckoutStateQuery` is properly called

**2. Step progression not working**:
- Verify `initialStepChanged` ref is being used correctly
- Check step completion conditions in Redux state

**3. Plugin not loading**:
- Ensure plugin is listed in `src/plugins.js`
- Verify plugin component is available in `Component` enum
- Check plugin dependencies in `package.json`

**4. Authentication flow issues**:
- Check NextAuth configuration
- Verify callback URLs are properly set
- Ensure session status is being handled correctly

### Debugging Tips

**1. Redux DevTools**: Monitor checkout state changes
**2. Network Tab**: Check API calls and responses
**3. Console Logs**: Add logging for step transitions
**4. Plugin Logging**: Enable plugin-specific debug logs

## Performance Considerations

### Code Splitting

- Use dynamic imports for step components
- Lazy load payment provider components
- Split plugin bundles appropriately

### API Optimization

- Use RTK Query caching effectively
- Invalidate tags only when necessary
- Implement proper loading states

### Bundle Size

- Tree-shake unused plugins
- Optimize payment provider scripts
- Monitor bundle size with webpack-bundle-analyzer

When working on checkout functionality, always consider the complete user journey, handle edge cases gracefully, and ensure compatibility with the extensive plugin ecosystem.
