npm package: @supplyworld/checkout
Description: Standalone pluggable checkout component for React / Next.js apps
Author: Supply World
Latest version: 1.2.0

# @supplyworld/checkout

> Standalone, pluggable checkout component for React & Next.js e-commerce apps.

One component. Drop it into any project. Get a fully working 4-step checkout with validation, shipping options, and payment methods — out of the box.

---

## Installation

```bash
# npm
npm install @supplyworld/checkout

# yarn
yarn add @supplyworld/checkout

# pnpm
pnpm add @supplyworld/checkout
```

---

## Quick Start

```tsx
import { CheckoutApp } from "@supplyworld/checkout";

const cart = [
  { id: 1, name: "Wireless Headphones", variant: "Black", price: 249.00, qty: 1, image: "🎧" },
  { id: 2, name: "Smart Mug",           variant: "14oz",  price: 129.95, qty: 2, image: "☕" },
];

export default function CheckoutPage() {
  return (
    <CheckoutApp
      cart={cart}
      onSuccess={(order) => {
        // POST to your API, redirect, etc.
        console.log("Order placed:", order);
      }}
      onCancel={() => {
        window.location.href = "/cart";
      }}
    />
  );
}
```

---

## Next.js Integration

### App Router (Next.js 13+)

Create `app/checkout/page.tsx`:

```tsx
"use client";

import { CheckoutApp } from "@supplyworld/checkout";
import { useRouter } from "next/navigation";
// import { useCartStore } from "@/store/cart"; // your state manager

export default function CheckoutPage() {
  const router = useRouter();
  // const cart = useCartStore((s) => s.items);

  return (
    <CheckoutApp
      cart={cart}
      onSuccess={async (order) => {
        await fetch("/api/orders", {
          method: "POST",
          body: JSON.stringify(order),
          headers: { "Content-Type": "application/json" },
        });
        router.push("/order-confirmed");
      }}
      onCancel={() => router.push("/cart")}
    />
  );
}
```

### Pages Router (Next.js 12 and below)

Create `pages/checkout.tsx` — same code but **without** the `"use client"` directive at the top.

---

## Embedding in a Modal

Use the `embedded` prop to remove the full-viewport wrapper:

```tsx
import { CheckoutApp } from "@supplyworld/checkout";

export function CheckoutModal({ isOpen, onClose, cart }) {
  if (!isOpen) return null;

  return (
    <div style={{
      position: "fixed", inset: 0,
      background: "rgba(0,0,0,0.85)",
      backdropFilter: "blur(6px)",
      zIndex: 9999, overflowY: "auto",
      padding: "40px 16px",
    }}>
      <CheckoutApp
        cart={cart}
        embedded
        onSuccess={(order) => { console.log(order); onClose(); }}
        onCancel={onClose}
      />
    </div>
  );
}
```

---

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `cart` | `CartItem[]` | demo data | Your cart line items |
| `onSuccess` | `(order: OrderData) => void \| Promise<void>` | — | Called after user places order |
| `onCancel` | `() => void` | — | Called when user clicks Cancel |
| `embedded` | `boolean` | `false` | Removes full-viewport wrapper |
| `shippingMethods` | `ShippingMethodConfig[]` | standard/express/overnight | Custom shipping options |
| `taxRate` | `number` | `0.08` | Tax rate as decimal (e.g. `0.08` = 8%) |
| `currencySymbol` | `string` | `"$"` | Currency symbol in price displays |

---

## Types

```ts
interface CartItem {
  id: string | number;
  name: string;
  variant?: string;
  price: number;        // unit price
  qty: number;
  image?: string;       // emoji or image URL
}

interface OrderData {
  contact: ContactFields;
  shipping: ShippingFields;
  payment: PaymentFields;
  cart: CartItem[];
  totals: {
    subtotal: number;
    shipping: number;
    tax: number;
    total: number;
  };
}

interface ShippingMethodConfig {
  id: "standard" | "express" | "overnight";
  label: string;
  description: string;
  price: number;
  icon?: string;        // emoji
}
```

---

## Custom Shipping Methods

```tsx
<CheckoutApp
  cart={cart}
  shippingMethods={[
    { id: "standard",  label: "Economy",    description: "7–10 days",         price: 0,     icon: "📦" },
    { id: "express",   label: "DHL Express",description: "3–5 business days", price: 19.99, icon: "🚚" },
    { id: "overnight", label: "FedEx Same Day", description: "Today",         price: 49.99, icon: "⚡" },
  ]}
  onSuccess={handleOrder}
/>
```

---

## Advanced: useCheckout Hook

Need full control over the UI? Use the hook directly and bring your own components:

```tsx
import { useCheckout } from "@supplyworld/checkout";

function MyCustomCheckout({ cart }) {
  const {
    step, done, placing, errors,
    contact, shipping, payment, totals,
    next, back, placeOrder, reset,
    updateContact, updateShipping, updatePayment,
  } = useCheckout({
    cart,
    taxRate: 0.1,
    onSuccess: (order) => console.log(order),
  });

  return (
    <div>
      {/* Build any UI you want on top of the checkout logic */}
      <p>Step {step + 1} of 4</p>
      <p>Total: ${totals.total.toFixed(2)}</p>
      <button onClick={next}>Next</button>
    </div>
  );
}
```

---

## Publishing

### Public npm registry
```bash
# 1. Update the name in package.json to your package name
# 2. Login to npm
npm login

# 3. Build & publish
npm publish --access public
```

### Private registry (GitHub Packages)
```bash
# 1. Add to package.json:
#    "publishConfig": { "registry": "https://npm.pkg.github.com" }

# 2. Create a .npmrc in the consuming project:
#    @supplyworld:registry=https://npm.pkg.github.com
#    //npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN

# 3. Publish
npm publish
```

### Private registry (Verdaccio — self-hosted)
```bash
# Run Verdaccio locally or on your server
npx verdaccio

# Publish to it
npm publish --registry http://localhost:4873
```

---

## Versioning

Follow semantic versioning (`major.minor.patch`):

```bash
npm version patch   # 1.0.0 → 1.0.1  (bug fixes)
npm version minor   # 1.0.1 → 1.1.0  (new features, backwards compatible)
npm version major   # 1.1.0 → 2.0.0  (breaking changes)
```

After bumping the version, run `npm publish` again.

---

## Peer Dependencies

This package requires React 17+ in the host project. It does **not** bundle React — it uses whatever version your app already has.

```json
"peerDependencies": {
  "react": ">=17.0.0",
  "react-dom": ">=17.0.0"
}
```