skills/customer-crm/customer-accounts/SKILL.md
Let shoppers register, manage their profile, save multiple addresses, and view their full order history using your platform's built-in customer account system
npx skillsauth add finsilabs/awesome-ecommerce-skills customer-accountsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Customer accounts let shoppers save addresses for faster checkout, view order history, track shipments, and manage their profile. Every major e-commerce platform has this built in — Shopify, WooCommerce, and BigCommerce all provide account registration, login, address books, and order history without any custom development. The main decisions are: whether to make accounts optional or required, and whether to extend the platform's default account pages with additional functionality.
| Platform | Account System | Recommended Extension | |----------|---------------|----------------------| | Shopify | Native customer accounts (classic or new) | Customer Accounts Concierge or Flits for enhanced account pages | | WooCommerce | My Account page (built-in, customizable) | YITH WooCommerce Wishlist; WooCommerce Memberships for gated content | | BigCommerce | Customer account portal (built-in) | Customer Groups for tiered access; LoyaltyLion for loyalty integration | | Custom / Headless | Build with JWT sessions and bcrypt password hashing | Required for complete control over authentication and account UX |
Shopify offers two account experiences: Classic accounts and the newer New customer accounts (available on all plans).
Enable accounts and choose the experience:
Classic accounts setup:
/account — your theme controls the layoutMaking accounts optional (strongly recommended):
Extending account pages:
For enhanced account functionality (wishlist, loyalty points, social login, recent orders with tracking):
WooCommerce has a built-in My Account page that includes order history, addresses, and profile management.
Set up My Account:
/my-account/Customize My Account tabs:
The default tabs are: Dashboard, Orders, Downloads, Addresses, Account details, Logout. Add or remove tabs:
woocommerce_account_menu_items filter in your child theme's functions.php, or install a plugin like YITH WooCommerce Customize My Account PageAddress book:
Wishlist:
Converting guest to registered after purchase:
WooCommerce shows a "Create account" prompt in order confirmation emails automatically when accounts are enabled but optional.
BigCommerce has a built-in customer portal.
Enable and configure:
Customer groups:
Use customer groups for tiered access, B2B pricing, or member-only categories:
Address book:
For headless storefronts, build a complete account system with secure authentication:
// lib/auth.ts
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { z } from 'zod';
const registerSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(128),
firstName: z.string().min(1).max(100),
lastName: z.string().min(1).max(100),
acceptsMarketing: z.boolean().default(false),
});
// POST /api/customers/register
export async function register(req: Request, res: Response) {
const input = registerSchema.parse(req.body);
const existing = await db.customers.findByEmail(input.email.toLowerCase());
if (existing) return res.status(409).json({ error: 'An account with this email already exists' });
const passwordHash = await bcrypt.hash(input.password, 12); // Cost factor 12 minimum
const customer = await db.customers.create({ ...input, email: input.email.toLowerCase(), passwordHash });
await sendVerificationEmail(customer);
const token = jwt.sign({ sub: customer.id, type: 'customer' }, process.env.JWT_SECRET!, { expiresIn: '7d' });
res.status(201).json({ customer: omit(customer, ['passwordHash']), token });
}
// POST /api/customers/login
export async function login(req: Request, res: Response) {
const { email, password } = req.body;
const customer = await db.customers.findByEmail(email.toLowerCase());
// Use the same error for both "not found" and "wrong password" to prevent email enumeration
if (!customer || !customer.passwordHash) return res.status(401).json({ error: 'Invalid email or password' });
if (customer.status === 'disabled') return res.status(403).json({ error: 'This account has been disabled' });
const valid = await bcrypt.compare(password, customer.passwordHash);
if (!valid) return res.status(401).json({ error: 'Invalid email or password' });
const token = jwt.sign({ sub: customer.id, type: 'customer' }, process.env.JWT_SECRET!, { expiresIn: '7d' });
res.json({ customer: omit(customer, ['passwordHash']), token });
}
// Address book CRUD — GET /api/customers/me/addresses
export async function listAddresses(req: AuthRequest, res: Response) {
const addresses = await db.customerAddresses.findMany({ where: { customerId: req.customerId } });
res.json({ addresses });
}
// POST /api/customers/me/addresses
export async function addAddress(req: AuthRequest, res: Response) {
const existing = await db.customerAddresses.findMany({ where: { customerId: req.customerId } });
const input = { ...addressSchema.parse(req.body), customerId: req.customerId };
if (input.isDefault || existing.length === 0) {
await db.customerAddresses.updateMany({ where: { customerId: req.customerId }, data: { isDefault: false } });
input.isDefault = true;
}
const address = await db.customerAddresses.create({ data: input });
res.status(201).json({ address });
}
// GET /api/customers/me/orders — paginated order history with tracking
export async function listOrders(req: AuthRequest, res: Response) {
const page = parseInt(req.query.page as string) || 1;
const limit = Math.min(parseInt(req.query.limit as string) || 10, 50);
const [orders, total] = await Promise.all([
db.orders.findMany({ where: { customerId: req.customerId }, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { lineItems: true, shipments: true } }),
db.orders.count({ where: { customerId: req.customerId } }),
]);
res.json({ orders, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) } });
}
The highest-converting moment to ask for account creation is immediately after a successful first purchase, not before.
Shopify: The order confirmation page includes a "Create account" button automatically when customer accounts are enabled but optional. Customize the message in Online Store → Themes → Customize → Order status page.
WooCommerce: Customize the "Thank you" page message in WooCommerce → Settings → Accounts or use the Checkout Field Editor plugin to add a post-checkout account creation prompt.
What to say: "Save your details for faster checkout next time" is more compelling than "Create an account" — focus on the benefit, not the action.
| Problem | Solution | |---------|----------| | Customers can't see past orders after creating an account | On Shopify, past orders from guest checkout are linked when the customer registers with the same email; verify the account linking is enabled in Settings → Customer accounts | | Address validation fails for international customers | Don't mark state/province as required — many countries don't have them; WooCommerce handles this with country-dependent field visibility | | JWT tokens too long-lived | Use 15-minute access tokens with refresh tokens for better security, or use server-side sessions with a secure, HttpOnly cookie | | No way to merge duplicate customer records | Shopify and WooCommerce both support customer merge in the admin; for custom builds, build a merge tool that consolidates orders and addresses |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion