Skip to content

Commit 8231a6b

Browse files
authored
feat: next account checkout (#421)
* feat: add next-account-checkout * feat: bugfixing checkout * fix: imports * feat: simplify styling * feat: align cart with spa-guest-checkout * feat: support shipping * feat: theme app the same as spa-guest-checkout * feat: update README * feat: remove unused components * feat: simplify styling again * feat: remove unused components * fix: remove log * feat: remove the forgotten password link * fix: delete random U
1 parent 732ff65 commit 8231a6b

28 files changed

+3785
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Account Checkout Next.js Example
2+
3+
This example showcases how to implement an **account checkout** flow with **Elastic Path Commerce Cloud** in a Next.js application with server-side authentication.
4+
5+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Felasticpath%2Fcomposable-frontend%2Ftree%2Fmain%2Fexamples%2Fnext-account-checkout&env=NEXT_PUBLIC_EPCC_CLIENT_ID,NEXT_PUBLIC_EPCC_ENDPOINT_URL,NEXT_PUBLIC_PASSWORD_PROFILE_ID,NEXT_PUBLIC_APP_URL&project-name=ep-next-account-checkout)
6+
7+
> **Heads-up:** This project can be considered a **hybrid** of the [Shopper Accounts Authentication Example](../shopper-accounts-authentication) and [Guest Checkout SPA Example](../spa-guest-checkout). It combines account authentication with cart management and checkout—providing a more secure, server-side approach to e-commerce checkout flows.
8+
9+
Key capabilities demonstrated:
10+
11+
1. **Account Authentication** – secure login with server-side token management using HttpOnly cookies.
12+
2. **User Login** – complete authentication flow with EPCC's account members API.
13+
3. **Protected Account Pages** – route protection ensuring authenticated access to account features.
14+
4. **Session Management** – automatic token refresh and secure session handling with cookies.
15+
5. **Cart Persistence** – automatically creates / retrieves a cart via the Shopper SDK with account association.
16+
6. **Account Checkout Form** – captures billing & shipping addresses with account token security:
17+
_Same as billing_ toggle
18+
• Optional _Delivery Instructions_ field (shipping only)
19+
**Server-side validation** and checkout processing
20+
7. **Order Creation** – converts a cart to an order using account tokens for enhanced security.
21+
8. **Shipping Options** – integrated shipping selection with real-time pricing updates.
22+
9. **Event-driven Cart Refresh** – broadcasts a `cart:updated` custom event so any part of the app can update after operations.
23+
10. **Centralized API Client Configuration** – server-side API calls with consistent authentication patterns.
24+
25+
> **Security Considerations:** Unlike the SPA guest checkout example, this is a Next.js implementation. This is because account checkout necessarily uses account tokens. Account tokens have elevated permissions compared to implicit tokens, so using them server-side only via Next.js server actions, is safer.
26+
27+
---
28+
29+
## Project Structure
30+
31+
```
32+
next-account-checkout/
33+
├── src/
34+
│ ├── app/
35+
│ │ ├── (auth)/ # Authentication pages (login/register/password-reset)
36+
│ │ ├── account/ # Account management pages
37+
│ │ ├── api/webhooks/ # Webhook endpoints for notifications
38+
│ │ ├── actions.ts # Server actions for cart/checkout operations
39+
│ │ ├── layout.tsx # Root layout with CartProvider
40+
│ │ └── page.tsx # Main page with product grid + cart
41+
│ ├── components/
42+
│ │ ├── cart-provider.tsx # Initializes / persists cart with account context
43+
│ │ ├── cart-view.tsx # Cart listing, promo codes & shipping options
44+
│ │ ├── checkout-view.tsx # Account checkout form (billing + shipping)
45+
│ │ ├── product-grid.tsx # Product display with add-to-cart
46+
│ │ └── ui.tsx # Reusable UI components
47+
│ └── lib/
48+
│ ├── auth.ts # Authentication helpers
49+
│ ├── auth-client.tsx # Client-side auth context
50+
│ └── api-client.ts # Server-side API client configuration
51+
└── README.md # ← you are here
52+
```
53+
54+
## How It Works
55+
56+
### 1. Account Authentication & Cart Initialization
57+
58+
The app combines account authentication with cart management. On app load, if a user is authenticated, their account token is used to create/retrieve a cart with enhanced permissions:
59+
60+
```tsx
61+
// src/components/cart-provider.tsx
62+
useEffect(() => {
63+
initializeCart() // Uses account token if available
64+
}, [])
65+
```
66+
67+
Account tokens provide elevated security compared to implicit tokens, enabling secure checkout operations.
68+
69+
### 2. Server-Side Cart Operations
70+
71+
All cart operations use Next.js server actions with account token authentication:
72+
73+
```ts
74+
// src/app/actions.ts
75+
export async function addToCart(productId: string, quantity: number = 1) {
76+
// Server-side operation with account token security
77+
}
78+
```
79+
80+
### 3. Account Checkout Form
81+
82+
`CheckoutView` manages a structured form state using direct SDK types:
83+
84+
```ts
85+
{
86+
customer: { email },
87+
billing_address: { first_name, last_name, line_1, city, region, postcode, country },
88+
shipping_address: { first_name, last_name, line_1, city, region, postcode, country, instructions },
89+
sameAsBilling: true
90+
}
91+
```
92+
93+
The form uses SDK field names directly with `satisfies` validation for type safety.
94+
95+
### 4. Shipping Integration
96+
97+
Shipping options are managed as custom cart items, for example purposes:
98+
99+
```ts
100+
const SHIPPING_OPTIONS = [
101+
{ id: "standard", name: "Standard Shipping", price_cents: 500 },
102+
{ id: "express", name: "Express Shipping", price_cents: 1500 },
103+
]
104+
```
105+
106+
### 5. Secure Order Creation
107+
108+
Account checkout uses the account token:
109+
110+
```ts
111+
await checkoutWithAccountToken({
112+
cartId,
113+
customer: form.customer,
114+
billing_address: form.billing_address,
115+
shipping_address: finalShippingAddress,
116+
})
117+
```
118+
119+
### 6. Event-Driven Updates
120+
121+
After successful operations, the app broadcasts cart updates:
122+
123+
```ts
124+
window.dispatchEvent(new Event("cart:updated"))
125+
```
126+
127+
Any listeners automatically refresh their data, ensuring UI consistency.
128+
129+
---
130+
131+
## Running the Example Locally
132+
133+
1. **Install deps** (from the repo root):
134+
135+
```bash
136+
pnpm i # or npm install / yarn
137+
```
138+
139+
2. **Set environment variables** – create a `.env.local` file in `examples/next-account-checkout`:
140+
141+
```
142+
NEXT_PUBLIC_EPCC_ENDPOINT_URL=https://euwest.api.elasticpath.com
143+
NEXT_PUBLIC_EPCC_CLIENT_ID=your_client_id
144+
NEXT_PUBLIC_PASSWORD_PROFILE_ID=your_password_profile_id
145+
NEXT_PUBLIC_APP_URL=http://localhost:3000
146+
```
147+
148+
3. **Start Next.js dev server**:
149+
150+
```bash
151+
pnpm --filter next-account-checkout dev
152+
```
153+
154+
4. **Set up password profile** in your EPCC account to enable authentication.
155+
156+
### Authentication Flow
157+
158+
## Key Components
159+
160+
- `CartProvider`: Initializes cart with account context and manages authentication state
161+
- `CartView`: Displays cart contents, shipping options, and promotion code management
162+
- `CheckoutView`: Account checkout form with server-side validation
163+
- `ProductGrid`: Product display with secure add-to-cart operations
164+
- Server Actions: Secure cart/checkout operations using account tokens
165+
166+
## Authentication Flow
167+
168+
1. **Login**: Users authenticate via account member API
169+
2. **Token Storage**: Account tokens stored in HttpOnly cookies (server-side only)
170+
3. **Cart Association**: Cart operations use account tokens for enhanced security
171+
4. **Protected Checkout**: Checkout requires authentication and uses account privileges
172+
5. **Session Management**: Automatic token refresh and secure session handling
173+
174+
## Security Considerations
175+
176+
- **Account tokens** provide elevated permissions compared to implicit tokens
177+
- **Server-side operations** prevent token exposure to client
178+
- **HttpOnly cookies** protect against XSS attacks
179+
- **Server actions** ensure secure cart/checkout processing
180+
- **Form validation** performed on both client and server
181+
182+
## Learn More
183+
184+
To learn more about EPCC account authentication, visit the [Elastic Path documentation](https://documentation.elasticpath.com/commerce-cloud/docs/developer/authentication/index.html).
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
};
6+
7+
export default nextConfig;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "next-account-checkout",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint"
10+
},
11+
"dependencies": {
12+
"@epcc-sdk/sdks-shopper": "workspace:*",
13+
"next": "15.3.1",
14+
"react": "^19.0.0",
15+
"react-dom": "^19.0.0",
16+
"zod": "^3.22.4"
17+
},
18+
"devDependencies": {
19+
"@tailwindcss/postcss": "^4",
20+
"@types/node": "^20",
21+
"@types/react": "^19",
22+
"@types/react-dom": "^19",
23+
"tailwindcss": "^4",
24+
"typescript": "^5"
25+
}
26+
}

0 commit comments

Comments
 (0)